你能克隆一个闭包吗? [英] Can you clone a closure?

查看:87
本文介绍了你能克隆一个闭包吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

出于显而易见的原因,无法克隆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< ...> 










$ b

Humm,好的,这里是我想做的一个例子:

 #! 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 named clone

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 implements Clone without requiring its internal type to be Clone, since Clone just creates a new pointer to the same memory), and so you can have a single Fn object that gets used in multiple threads, no need to duplicate it.

In summary, put your WithCall in an Arc 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));
    });
}

playpen

Note: I've removed the unboxed_closures feature gate by using the recommended sugar Fn(...) -> ... for types and direct () calls (not using .call). Also, I've removed the unnecessary unsafe impl Send, since Send is automatically implemented if the contents are. unsafe impls are only required if the contents are not Send 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, since Fn::call takes &self. The mut 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, but

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));
}

(This is partly due to &T being Copy 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 or self as for Fn and FnOnce respectively). If you create a struct and implement FnMut manually, you can still implement Clone 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆