由于显而易见的原因,无法克隆FnMut
闭包,但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");
}
我有一个带有盒装闭包的结构,我需要将该结构传递给多个线程。我不能,但我也无法克隆它,因为你无法克隆Box<Fn<>>
而你无法克隆&Fn<...>
。
答案 0 :(得分:11)
您要做的是从多个线程调用闭包。也就是说,跨多个线程共享闭包。一旦短语&#34;分享多个线程&#34;在我脑海中浮现,我的第一个想法是to reach for Arc
(至少在以某种形式实现RFC 458之前,当&
可以跨线程使用时)。
这允许安全的共享内存(它实现Clone
而不要求其内部类型为Clone
,因为Clone
只是创建一个指向同一内存的新指针),所以你可以有一个Fn
对象在多个线程中使用,不需要复制它。
总之,请将WithCall
放入Arc
并克隆。{/ p>
use std::sync::Arc;
use std::thread;
type Fp = Box<Fn(i8, i8) -> i8 + Send + Sync>;
struct WithCall {
fp: Fp,
}
impl WithCall {
pub fn new(fp: Fp) -> WithCall {
WithCall { fp }
}
pub fn run(&self, a: i8, b: i8) -> i8 {
(self.fp)(a, b)
}
}
fn main() {
let adder = WithCall::new(Box::new(|a, b| a + b));
println!("{}", adder.run(1, 2));
let add_a = Arc::new(adder);
let add_b = add_a.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");
}
旧答案(这仍然相关):拥有&mut Fn
特质对象是非常不寻常的,因为Fn::call
需要&self
。 mut
不是必需的,我认为它增加了额外的功能。拥有&mut Box<Fn()>
会增加一些功能,但这也很不寻常。
如果您更改为&
指针而不是&mut
,则更自然地工作(&Fn
和&Box<Fn>
)。如果没有看到您正在使用的实际代码,很难确切地说出您正在做什么,但
fn call_it(f: &Fn()) {
(*f)();
(*f)();
}
fn use_closure(f: &Fn()) {
call_it(f);
call_it(f);
}
fn main() {
let x = 1i32;
use_closure(&|| println!("x is {}", x));
}
(部分原因是&T
为Copy
,部分原因是重新借款;它也适用于&mut
。)
或者,您可以关闭闭包,这可能适用于更多情况:
fn foo(f: &Fn()) {
something_else(|| f())
}
由于显而易见的原因,无法克隆
FnMut
关闭。
没有FnMut
无法克隆的固有原因,它只是一个包含某些字段的结构(以及一个需要&mut self
的方法,而不是&self
或self
分别为Fn
和FnOnce
。如果您手动创建结构并实施FnMut
,则仍可以为其实现Clone
。
或者以某种方式将原始指针传递给Fn是安全的,例如:
let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>>
从技术上讲,上述工作,但似乎很奇怪。
从技术上讲,如果你小心确保Rust的别名和生命周期要求得到满足,那么它是有效的...但是通过选择不安全的指针你会给自己带来负担,而不是让编译器帮忙您。对编译器错误的正确响应是使用unsafe
代码,而不是深入研究错误并调整代码以使其更有意义(对于编译器来说,通常会导致编译错误)是相对罕见的。对人类更有意义)。
答案 1 :(得分:5)
如果所有捕获的变量都有,则闭包实现Copy
和Clone
。您可以重写代码以使用泛型而不是盒装特征对象来克隆它:
use std::thread;
#[derive(Clone)]
struct WithCall<F> {
fp: F,
}
impl<F> WithCall<F>
where
F: Fn(i8, i8) -> i8,
{
pub fn new(fp: F) -> Self {
WithCall { fp }
}
pub fn run(&self, a: i8, b: i8) -> i8 {
(self.fp)(a, b)
}
}
fn main() {
let adder = WithCall::new(|a, b| a + b);
println!("{}", adder.run(1, 2));
let add_a = adder.clone();
let add_b = adder;
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");
}
请记住,闭包捕获了他们的环境,因此他们根据环境拥有自己的生命周期。但是,您可以引用Fn*
并进一步传递它们,或将它们存储在结构中:
fn do_more<F>(f: &F) -> u8
where
F: Fn(u8) -> u8,
{
f(0)
}
fn do_things<F>(f: F) -> u8
where
F: Fn(u8) -> u8,
{
// We can pass the reference to our closure around,
// effectively allowing us to use it multiple times.
f(do_more(&f))
}
fn main() {
let val = 2;
// The closure captures `val`, so it cannot live beyond that.
println!("{:?}", do_things(|x| (x + 1) * val));
}
我想说,由于生命周期的考虑,将Fn*
转换为原始指针并传递它并不是普遍安全的。
答案 2 :(得分:0)
这是1.22.1
中的工作代码目的是使这项工作成功。
let x = |x| { println!("----{}",x)};
let mut y = Box::new(x);
y.clone();
使用了顶部建议的原始代码。
我开始克隆Fn
关闭。
type Fp = Box<Fn(i8, i8) -> i8 + Send + Sync>;
在结构Arc
Fp
周围添加WithCall