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

5
我想让一个函数在多个线程间共享,还需要能够对该函数使用另一个函数进行调用:
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(())
    });
}

调用这个函数后,它使用传递的 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

你可以将这个例子简化为 fn main() { let ff = |f: &dyn FnOnce()| { f(); }; },并且仍然会得到基本相同的错误信息。 - Michael Anderson
1个回答

4

我认为有几个问题导致了你遇到这个问题。

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

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

问题的核心在于 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.
}

首先,当调用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

}

所以,如果你可以减少函数类型的限制,使其只是 Fn 或者 FnMut 中的一个,那么这应该能解决你的问题,如果不能...请继续阅读。
你可以将一个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`"
}

这相当于

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

然而,我们无法以一种有帮助的方式混合使用泛型和闭包。尝试使用 |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`"
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接