cannot move a value of type dyn for<'r> FnOnce(&'r mut [u8]): the size of dyn for<'r> FnOnce(&'r mut [u8]) cannot be statically determined

Viewed 918

I'm trying to have a function shared between threads, that can be called with another function on it:

fn main() {
    let on_virtual_tun_write = std::sync::Arc::new(|f: &dyn FnOnce(&mut [u8]), size: usize|-> std::result::Result<(),()>{
        let mut buffer = vec![0; size];
        f(buffer.as_mut_slice());
        Ok(())
    });
}

Once it's called, it uses the f passed as an argument, to retrieve a buffer. As you can see, I do not intend to copy the function, I just use it as soon as someone calls the closure, and discards it. However, Rust thinks that I want to copy it. What can I do to tell Rust that I just want to use a reference to the function? I thought &dyn was sufficient.

Error:

error[E0161]: cannot move a value of type dyn for<'r> FnOnce(&'r mut [u8]): the size of dyn for<'r> FnOnce(&'r mut [u8]) cannot be statically determined
 --> src/main.rs:4:9
  |
4 |         f(buffer.as_mut_slice());
  |         ^

error[E0507]: cannot move out of `*f` which is behind a shared reference
 --> src/main.rs:4:9
  |
4 |         f(buffer.as_mut_slice());
  |         ^ move occurs because `*f` has type `dyn for<'r> FnOnce(&'r mut [u8])`, which does not implement the `Copy` trait

error: aborting due to 2 previous errors; 1 warning emitted
1 Answers

So I think there's a few issues that are coming together to cause you this problem.

I'm going to use a reduced example which generates essentially the same error message.

fn main() { 
  let ff = |f: &dyn FnOnce()| { f(); };
}

The core of the problem is that rust is stopping bad code where the FnOnce is used more than once. Like this (which doesn't compile):

fn main() {
  let f = || {  }; // Imagine this is a real FnOnce()
  let ff = |f: &dyn FnOnce()| { f(); };
  ff(&f); // Use the FnOnce in our wrapper
  f(); // Use the FnOnce again - should fail to compile.
}

Firstly FnOnce() when called consumes its self - this is how it ensures it can only be called once. Which means you need to be passing the object, not a reference to it - otherwise the caller can still be holding a reference. This is not the case for Fn() and FnMut(). The first uses only a reference to self, and the second a mutable reference to self. This means the following compile OK:

fn main() { 
    let ff = |f: &dyn Fn()| { f(); };
    let f = || {};
    ff(f); // First call is OK
    f(); // Second usage is OK - it's a pure function

    let ff = |f: &mut dyn FnMut()| { f(); };
    let mut i = 0;
    let mut f = || { i += 1 };
    ff(&mut f); // OK i=1
    f(); // OK i=2
    println!("i={}", i); // prints 2

}

So if you can reduce the restrictions on your function type so that it is either Fn or FnMut, that should solve it for you, if not ... read on.

You can pass an instance (not a reference) of a FnOnce to a generic function that uses impl. e.g.

fn ff(f: impl FnOnce()) {
  f()
}

fn main() {
   // Creating a closure that moves q into it ensures we get a FnOnce
   let q=vec![1,2,3];
   let f = move || {println!("{}", q.len())};

   ff(f); // OK.
   // f(); // Compile error with "use of moved value: `f`"
}

this is equivalent to

fn<F> ff(f: F)
    where F: FnOnce()
{
  f()
}

However, we can't mix generics and closures in a way that can help us. Trying to use |f: impl FnOnce()| generates the error : error[E0562]: impl Trait not allowed outside of function and inherent method return types.

The only way I think you can use both the FnOnce and closure is to box up the function and pass it in. That will transfer ownership of the function into the closure and so we call it. For example the following compiles:

fn main() {
   let ff = |f: Box<dyn FnOnce()>| { f() };
   let q=vec![1,2,3];
   let f = Box::new(move || {println!("{}", q.len())});
   ff(f); // OK
   // f(); // Errors with "use of moved value: `f`"
}
Related