你能克隆一个闭包吗? [英] Can you clone a closure?
问题描述
出于显而易见的原因,无法克隆FnMut。
但是,Fn有一个不可变的作用域;是否有某种方法来创建Fn的重复?
尝试克隆它会导致:
的范围内实现任何方法
& mut core :: ops :: Fn(logger :: Level,&'a collections :: string :: String)+发送
未在名为clone
以某种方式将原始指针传递给Fn,例如:
let func_pnt =& mut Box< Fn< > +发送> as * mut Box< Fn< ...>
$ bHumm,好的,这里是我想做的一个例子:
#! unboxed_closures]]
使用std :: thread :: Thread;
use std :: clone :: Clone;
struct WithCall {
fp:Box< Fn<(i8,i8),i8> +发送>
}
impl WithCall {
pub fn new(fp:Box< Fn<(i8,i8),i8> + Send> WithCall {
return WithCall {
fp:fp
};
}
pub fn run(& self,a:i8,b:i8) - > i8 {
return self.fp.call((a,b));
}
}
unsafe impl发送给WithCall {}
impl克隆forCall {
fn clone(& self) > WithCall {
return WithCall {
fp:self.fp.clone()
};
}
}
fn main(){
let adder = WithCall :: new(Box :: new(|&:a:i8,b: i8 | - > i8 {
return a + b;
}));
println!({},adder.run(1,2));
let add_a = adder.clone();
let add_b = adder.clone();
Thread :: scoped(move || {
println!(In remote thread:{},add_a.run(10,10));
}
Thread :: scoped(move || {
println!(In remote thread:{},add_b.run(10,10));
}
}
你有一个带有盒子闭包的结构体,你需要将该结构体传递给多个任务;显然你不能。 ...但是你也不能克隆它,因为你不能克隆一个
Box< Fn<>
$ c>& Fn< ...>
playpen: http://is.gd/1oNPYJ
解决方案您要做的是调用闭包从多个线程。也就是说,在多个线程之间共享闭包。一旦跨多个线程共享这个短语贯穿我的头脑,我的第一个想法是覆盖
Arc
(至少直到 RFC 458 以某种形式实现,当&
将跨线程可用时)。这允许安全的共享内存(它实现Clone
,而不需要其内部类型为Clone
,因为Clone
只是创建了一个指向同一内存的新指针),所以你可以有一个Fn
对象在多线程中使用,
总之,将
WithCall
放在Arc
并克隆它。#![allow(unstable)]
使用std :: thread :: Thread;
use std :: sync :: Arc;
type Fp = Box< Fn(i8,i8) - > i8 + Send + Sync> ;;
struct WithCall {
fp:Fp
}
impl WithCall {
pub fn new(fp:Fp) - > WithCall {
WithCall {fp:fp}
}
pub fn run(& self,a:i8,b:i8) - > i8 {
(* self.fp)(a,b)
}
}
fn main(){
let adder = WithCall :: new(Box :: new(|&:a:i8,b | a + b));
println!({},adder.run(1,2));
let add_a = Arc :: new(adder);
let add_b = add_a.clone();
Thread :: scoped(move || {
println!(In remote thread:{},add_a.run(10,10));
}
Thread :: scoped(move || {
println!(In remote thread:{},add_b.run(10,10));
}
}
注意:我已使用推荐的糖
Fn(...) - >移除了
用于类型和直接unboxed_closures
...()
调用(不使用.call
)。此外,我删除了不必要的unsafe impl Send
,因为发送
是自动实现的,如果内容是。默认情况下,如果内容不是Send
,并且程序员希望覆盖编译器的保守判断,则只需要unsafe impl
。
旧的答案(这还是相关的):有一个
; mut Fn
trait对象,因为Fn :: call
需要& self
。mut
不是必需的,我认为它增加了额外的零额外的功能。有& mut Box< Fn()>
确实添加了一些功能,但也不常见。
如果你更改为
&
指针而不是& mut
code>& 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));
}
(这部分是由于
& / code>
复制
,部分原因是reborrowing;它也适用于& mut
)
或者,您可以关闭闭包,这可能适用于更多情况:
fn foo(f:& Fn()){
something_else(|| f())
}
显然,由于显而易见的原因,FnMut不能被克隆。
没有固有的原因,一个
FnMut
无法克隆,它只是一个结构与一些字段(和一个方法,& mut self
,而不是& self
或self
$ c> Fn 和FnOnce
)。如果你创建一个结构并手动实现FnMut
,你仍然可以实现Clone
。
或者以某种方式传递一个指向Fn的原始指针是安全的,例如:
let func_pnt =& mut Box< Fn< ...> +发送> as * mut Box< Fn< ...>
blockquote>
技术上,它是有效的,如果你仔细确保Rust的别名和生存期的要求满足...但通过选择不安全的指针,你把这种负担自己,不让编译器帮助你。对编译器错误的正确响应是相对少见的,是使用
unsafe
代码,而不是深入研究错误和调整代码,使它更有意义编译器,这往往导致它对人类更有意义)。A FnMut cannot be cloned, for obvious reasons.
However, a Fn has an immutable scope; is there some way to create a 'duplicate' of a Fn?
Trying to clone it results in:
&mut core::ops::Fn(logger::Level, &'a collections::string::String) + Send
does not implement any method in scope namedclone
Or is it safe to somehow pass a raw pointer to a Fn around, like:
let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>>
Technically the above works, but it seems quite weird.
Humm, ok, here's an example of what I'm trying to do:
#![feature(unboxed_closures)] use std::thread::Thread; use std::clone::Clone; struct WithCall { fp:Box<Fn<(i8,i8), i8> + Send> } impl WithCall { pub fn new(fp:Box<Fn<(i8,i8), i8> + Send>) -> WithCall { return WithCall { fp: fp }; } pub fn run(&self, a:i8, b:i8) -> i8 { return self.fp.call((a, b)); } } unsafe impl Send for WithCall {} impl Clone for WithCall { fn clone(&self) -> WithCall { return WithCall { fp: self.fp.clone() }; } } fn main() { let adder = WithCall::new(Box::new(|&:a:i8, b:i8| -> i8 { return a + b; })); println!("{}", adder.run(1, 2)); let add_a = adder.clone(); let add_b = adder.clone(); Thread::scoped(move || { println!("In remote thread: {}", add_a.run(10, 10)); }); Thread::scoped(move || { println!("In remote thread: {}", add_b.run(10, 10)); }); }
ie. You have a struct with a boxed closure in it, you need to pass that struct to a number of tasks; obviously you can't. ...but you also can't clone it, because you can't clone a
Box<Fn<>>
and you can't clone a&Fn<...>
playpen: http://is.gd/1oNPYJ
解决方案What you are trying to do is call a closure from multiple threads. That is, share the closure across multiple threads. As soon as the phrase "share across multiple threads" cross my mind, my first thought is to reach for
Arc
(at least until RFC 458 is implemented in some form, when&
will become usable across threads). This allows for safe shared memory (it implementsClone
without requiring its internal type to beClone
, sinceClone
just creates a new pointer to the same memory), and so you can have a singleFn
object that gets used in multiple threads, no need to duplicate it.In summary, put your
WithCall
in anArc
and clone that.#![allow(unstable)] use std::thread::Thread; use std::sync::Arc; type Fp = Box<Fn(i8,i8) -> i8 + Send + Sync>; struct WithCall { fp: Fp } impl WithCall { pub fn new(fp: Fp) -> WithCall { WithCall { fp: fp } } pub fn run(&self, a: i8, b: i8) -> i8 { (*self.fp)(a, b) } } fn main() { let adder = WithCall::new(Box::new(|&: a: i8, b| a + b)); println!("{}", adder.run(1, 2)); let add_a = Arc::new(adder); let add_b = add_a.clone(); Thread::scoped(move || { println!("In remote thread: {}", add_a.run(10, 10)); }); Thread::scoped(move || { println!("In remote thread: {}", add_b.run(10, 10)); }); }
Note: I've removed the
unboxed_closures
feature gate by using the recommended sugarFn(...) -> ...
for types and direct()
calls (not using.call
). Also, I've removed the unnecessaryunsafe impl Send
, sinceSend
is automatically implemented if the contents are.unsafe impl
s are only required if the contents are notSend
by default and the programmer wishes to overriding the compiler's conservative judgement.
Old answer (this is still relevant): It is quite unusual to have a
&mut Fn
trait object, sinceFn::call
takes&self
. Themut
is not necessary, and I think it adds literally zero extra functionality. Having a&mut Box<Fn()>
does add some functionality, but it is also unusual.If you change to a
&
pointer instead of an&mut
things will work more naturally (with both&Fn
and&Box<Fn>
). Without seeing the actual code you're using, it's extremely hard to tell exactly what you're doing, butfn 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)); }
(This is partly due to
&T
beingCopy
and also partly due to reborrowing; it works with&mut
as well.)Alternatively, you can close-over the closure, which likely works in more situations:
fn foo(f: &Fn()) { something_else(|| f()) }
Obviously a FnMut cannot be cloned, for obvious reasons.
There's no inherent reason a
FnMut
can't be cloned, it's just a struct with some fields (and a method that takes&mut self
, rather than&self
orself
as forFn
andFnOnce
respectively). If you create a struct and implementFnMut
manually, you can still implementClone
for it.Or is it safe to somehow pass a raw pointer to a Fn around, like:
let func_pnt = &mut Box<Fn<...> + Send> as *mut Box<Fn<...>>
Technically the above works, but it seems quite weird.
Technically it works if you're careful to ensure the aliasing and lifetime requirements of Rust are satisfied... but by opting in to unsafe pointers you're putting that burden on yourself, not letting the compiler help you. It is relatively rare that the correct response to a compiler error is to use
unsafe
code, rather than delving in to the error and tweaking the code to make it make more sense (to the compiler, which often results in it making more sense to humans).这篇关于你能克隆一个闭包吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!