由于显而易见的原因,无法克隆 FnMut
闭包,但 Fn
闭包具有不可变的范围;有没有办法创建一个 Fn
Fn
闭包?
尝试克隆它会导致:
error[E0599]: no method named `clone` found for type `std::boxed::Box<std::ops::Fn(i8, i8) -> i8 + std::marker::Send + 'static>` in the current scope
--> src/main.rs:22:25
|
22 | fp: self.fp.clone(),
| ^^^^^
|
= note: self.fp is a function, perhaps you wish to call it
= note: the method `clone` exists but the following trait bounds were not satisfied:
`std::boxed::Box<std::ops::Fn(i8, i8) -> i8 + std::marker::Send> : std::clone::Clone`
以某种方式将原始指针传递给 Fn
是否安全,例如:
let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>>
从技术上讲,上述工作,但似乎很奇怪 .
这是我正在尝试做的一个例子:
use std::thread;
struct WithCall {
fp: Box<Fn(i8, i8) -> i8 + Send>,
}
impl WithCall {
pub fn new(fp: Box<Fn(i8, i8) -> i8 + Send>) -> WithCall {
WithCall { fp: fp }
}
pub fn run(&self, a: i8, b: i8) -> i8 {
(self.fp)(a, b)
}
}
impl Clone for WithCall {
fn clone(&self) -> WithCall {
WithCall {
fp: self.fp.clone(),
}
}
}
fn main() {
let adder = WithCall::new(Box::new(|a, b| a + b));
println!("{}", adder.run(1, 2));
let add_a = adder.clone();
let add_b = adder.clone();
let a = thread::spawn(move || {
println!("In remote thread: {}", add_a.run(10, 10));
});
let b = thread::spawn(move || {
println!("In remote thread: {}", add_b.run(10, 10));
});
a.join().expect("Thread A panicked");
b.join().expect("Thread B panicked");
}
我有一个带有盒装闭包的结构,我需要将该结构传递给许多线程 . 我可以't, but I also can'克隆它,因为你无法克隆 Box<Fn<>>
而你无法克隆 &Fn<...>
.
3 回答
你要做的是从多个线程调用一个闭包 . 也就是说,跨多个线程共享闭包 . 一旦短语"share across multiple threads"穿过我的脑海,我的第一个想法是to reach for Arc(至少直到RFC 458以某种形式实现,当
&
将在线程之间变得可用时) .这允许安全的共享内存(它实现
Clone
而不要求其内部类型为Clone
,因为Clone
只是创建一个指向同一内存的新指针),因此你可以拥有一个在多个线程中使用的单个Fn
对象,不需要复制它 .总之,将
WithCall
放在Arc
并克隆它 .playground
旧答案(这仍然相关):拥有一个
&mut Fn
特征对象是非常不寻常的,因为Fn::call需要&self
.mut
不是必需的,我认为它增加了额外的零功能 . 有一个&mut Box<Fn()>
确实增加了一些功能,但它也很不寻常 .如果您更改为
&
指针而不是&mut
,事情将更自然地工作(同时&Fn
和&Box<Fn>
) . 在没有看到实际代码的情况下,你很难确切地说出你在做什么,但是(部分原因是
&T
为Copy
,部分原因是重新借贷;它也适用于&mut
. )或者,您可以关闭闭包,这可能适用于更多情况:
没有内在的原因
FnMut
只能是一个带有一些字段的结构(并且一个方法需要&mut self
,而不是&self
或self
分别为Fn
和FnOnce
) . 如果您创建一个结构并手动实现FnMut
,您仍然可以为它实现Clone
.从技术上讲,如果你重新给自己增加负担,而不是让编译器帮助你,它就有效 . 对编译器错误的正确响应是使用
unsafe
代码是相对罕见的,而不是深入研究错误并调整代码以使其更有意义(对编译器来说,这通常会使对人类更有意义) ) .Rust 1.26
如果所有捕获的变量都执行,则闭包实现
Copy
和Clone
. 您可以重写代码以使用泛型而不是盒装特征对象来克隆它:在Rust 1.26之前
请记住,闭包捕获了他们的环境,因此他们根据环境拥有自己的生命周期 . 但是,您可以引用
Fn*
并进一步传递它们,或将它们存储在结构中:我会说,由于生命周期的考虑,将
Fn*
转换为原始指针并传递它并不是普遍安全的 .这是1.22.1中的工作代码
目的是使这项工作 .
使用了顶部建议的原始代码 .
我开始克隆一个
Fn
闭包 .结束在
WithCall
中的Fp
附近添加Arc
Play rust : working code Gist : working code in 1.22.1