Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is calling a FnOnce closure a move?

I'm trying to pass in a closure to a function that will then mutate something passed into it within the scope of the function. Based on my current understanding of Rust, that should look something like this:

pub fn call_something(callback: &FnOnce(&mut Vec<i32>)) {
    let mut my_vec = vec![0, 1, 2, 3, 4];
    callback(&mut my_vec);
}

That results in these errors:

error[E0161]: cannot move a value of type dyn for<'r> std::ops::FnOnce(&'r mut std::vec::Vec<i32>): the size of dyn for<'r> std::ops::FnOnce(&'r mut std::vec::Vec<i32>) cannot be statically determined
 --> src/lib.rs:3:5
  |
3 |     callback(&mut my_vec);
  |     ^^^^^^^^

error[E0507]: cannot move out of borrowed content
 --> src/lib.rs:3:5
  |
3 |     callback(&mut my_vec);
  |     ^^^^^^^^ cannot move out of borrowed content

Why is calling a FnOnce a move? What am I missing here?

like image 377
user96425 Avatar asked Nov 09 '18 22:11

user96425


1 Answers

Why is calling a FnOnce a move?

Because that's the definition of what makes a closure FnOnce:

extern "rust-call" fn call_once(self, args: Args) -> Self::Output
//                              ^^^^

Contrast this to FnMut and Fn:

extern "rust-call" fn call_mut(&mut self, args: Args) -> Self::Output
//                             ^^^^^^^^^
extern "rust-call" fn call(&self, args: Args) -> Self::Output
//                         ^^^^^

See also:

  • When does a closure implement Fn, FnMut and FnOnce?
  • "cannot move a value of type FnOnce" when moving a boxed function
  • Cannot move out of borrowed content

You probably want

pub fn call_something(callback: impl FnOnce(&mut Vec<i32>))

or

pub fn call_something<F>(callback: F)
where
    F: FnOnce(&mut Vec<i32>),

These are identical. They both take ownership of the closure, which means that you can call the closure and consume it in the process.

like image 141
Shepmaster Avatar answered Sep 29 '22 08:09

Shepmaster