我试图在线程之间共享一个函数,可以用另一个函数来调用它:
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)
所以我认为有几个问题共同导致了这个问题。
我将使用一个简化的示例,它生成基本上相同的错误消息。
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)
因此,如果您可以减少对函数类型的限制,使其为 或Fn,FnMut那么应该可以为您解决这个问题,如果不是……请继续阅读。
您可以将 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)
| 归档时间: |
|
| 查看次数: |
1632 次 |
| 最近记录: |