甲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`
Run Code Online (Sandbox Code Playgroud)
以某种方式将原始指针传递给Fn
周围是安全的,例如:
let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>>
Run Code Online (Sandbox Code Playgroud)
从技术上讲,上述工作,但似乎很奇怪.
这是我正在尝试做的一个例子:
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");
}
Run Code Online (Sandbox Code Playgroud)
我有一个带有盒装闭包的结构,我需要将该结构传递给许多线程.我不能,但我也无法克隆它,因为你无法克隆一个Box<Fn<>>
而你无法克隆一个&Fn<...>
.
huo*_*uon 11
你要做的是从多个线程调用一个闭包.也就是说,跨多个线程共享闭包.一旦"跨越多个线程共享"这个短语贯穿我的脑海,我的第一个想法就是达到目标Arc
(至少在RFC 458以某种形式实现,何时&
可以跨线程使用).
这允许安全的共享内存(它实现 Clone
不需要其内部类型Clone
,因为Clone
只创建一个指向同一内存的新指针),因此您可以拥有一个Fn
在多个线程中使用的单个对象,无需复制它.
总而言之,把你WithCall
放入Arc
并克隆它.
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");
}
Run Code Online (Sandbox Code Playgroud)
旧的答案(这仍然是相关的):有一个&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));
}
Run Code Online (Sandbox Code Playgroud)
(这部分是由于&T
存在Copy
而且部分原因是再借贷;它也适用&mut
.)
或者,您可以关闭闭包,这可能适用于更多情况:
fn foo(f: &Fn()) {
something_else(|| f())
}
Run Code Online (Sandbox Code Playgroud)
一个
FnMut
封闭无法克隆,出于显而易见的原因.
有没有内在的原因FnMut
不能被克隆,它只是一个带有某些字段(而这需要一个方法结构&mut self
,而不是&self
或self
作为Fn
和FnOnce
分别).如果您创建一个结构并FnMut
手动实现,您仍然可以实现Clone
它.
或者以某种方式将原始指针传递给Fn是安全的,例如:
Run Code Online (Sandbox Code Playgroud)let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>>
从技术上讲,上述工作,但似乎很奇怪.
从技术上讲它的工作原理,如果你很小心,以确保锈的混淆和寿命的要求得到满足......但是透过在不安全的指针,你把你自己说的负担,不会让你的编译器的帮助.这是比较罕见的,一个编译器错误正确答案是使用unsafe
代码,而不是在深入研究的错误和调整的代码,使其更有意义(编译器,其结果往往是使更多的意义,人类) .
闭包实现两者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");
}
Run Code Online (Sandbox Code Playgroud)
请记住,闭包捕获了他们的环境,因此他们根据环境拥有自己的生命周期.但是,您可以引用它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));
}
Run Code Online (Sandbox Code Playgroud)
我会说Fn*
,由于生命周期的考虑,将原始指针转换为原始指针并传递它并不是普遍安全的.