无法移动 dyn for<'r> FnOnce(&'r mut [u8]) 类型的值:无法静态确定 dyn for<'r> FnOnce(&'r mut [u8]) 的大小

Gat*_*ito 5 rust

我试图在线程之间共享一个函数,可以用另一个函数来调用它:

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(())
    });
}
Run Code Online (Sandbox Code Playgroud)

一旦被调用,它就会使用f传递的参数作为参数来检索缓冲区。正如你所看到的,我并不打算复制该函数,我只是在有人调用闭包时立即使用它,然后丢弃它。然而,Rust 认为我想复制它。我该怎么做才能告诉 Rust 我只想使用函数的引用?我以为&dyn就足够了。

错误:

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
Run Code Online (Sandbox Code Playgroud)

Mic*_*son 4

所以我认为有几个问题共同导致了这个问题。

我将使用一个简化的示例,它生成基本上相同的错误消息。

fn main() { 
  let ff = |f: &dyn FnOnce()| { f(); };
}
Run Code Online (Sandbox Code Playgroud)

问题的核心在于 Rust 正在阻止FnOnce多次使用的不良代码。像这样(无法编译):

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.
}
Run Code Online (Sandbox Code Playgroud)

首先,FnOnce()当被调用时会消耗它的自身 - 这就是它确保它只能被调用一次的方式。这意味着您需要传递对象,而不是对其的引用 - 否则调用者仍然可以持有引用。Fn()和 的情况并非如此FnMut()。第一个仅使用对 self 的引用,第二个使用对 self 的可变引用。这意味着以下编译成功:

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

}
Run Code Online (Sandbox Code Playgroud)

因此,如果您可以减少对函数类型的限制,使其为 或FnFnMut那么应该可以为您解决这个问题,如果不是……请继续阅读。

可以将 a 的实例(而不是引用)传递FnOnce给使用 的通用函数impl。例如

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`"
}
Run Code Online (Sandbox Code Playgroud)

这相当于

fn<F> ff(f: F)
    where F: FnOnce()
{
  f()
}
Run Code Online (Sandbox Code Playgroud)

但是,我们无法以对我们有帮助的方式混合泛型和闭包。尝试使用|f: impl FnOnce()|会生成错误:error[E0562]: impl Trait not allowed outside of function and inherent method return types

我认为可以同时使用FnOnce和 闭包的唯一方法是将函数装箱并将其传入。这会将函数的所有权转移到闭包中,因此我们称之为闭包。例如以下编译:

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`"
}
Run Code Online (Sandbox Code Playgroud)