如何用unboxed闭包替换proc?

时间:2015-03-01 09:43:27

标签: rust

我正在努力取代proc。使用Invoke有效,但我必须指定生命周期。为了能够将枚举发送到另一个线程,我必须使用'static和更晚的mem::transmute来转换生命周期。不太吸引人。

另一方面,使用FnOnce给出了这个错误:

<anon>:24:32: 24:33 error: cannot move a value of type for<'r> core::ops::FnOnce(&'r Type) + Send: the size of for<'r> core::ops::FnOnce(&'r Type) + Send cannot be statically determined [E0161]

如何正确替换proc?

#![feature(box_syntax)]
#![feature(std_misc)]

use std::mem;
use std::thunk::Invoke;

struct Type;

enum Message {
    Handle(Box<Invoke<(&'static Type)> + Send>) //'
}
enum Message2 {
    Handle(Box<FnOnce(&Type) + Send>)
}

pub fn main() {
    let a = Type;
    let handler = Message::Handle(box move |_| {});
    let handler2 = Message2::Handle(box move |_| {});
    match handler {
        Message::Handle(f) => f.invoke(unsafe {mem::transmute(&a)})
    }
    match handler2 {
        Message2::Handle(f) => f(&a)
    }
}

3 个答案:

答案 0 :(得分:3)

如何使用Thunk

#![feature(std_misc)]

use std::thunk::Thunk;

struct Type;

enum Message<'a> { //'
    Handle(Thunk<'a, &'a Type, u8>)
}

pub fn main() {
    let a = Type;
    let handler = Message::Handle(Thunk::with_arg(move |_| 42));
    let res = match handler {
        Message::Handle(f) => f.invoke((&a))
    };
    println!("{}", res);
}

playpen link

  

为了能够将枚举发送到另一个线程,我必须使用&#39;静态和后来的mem :: transmute来转换生命周期。

RFC 458登陆以来,thread::scoped可以接受非Fn*生命周期的闭包(和'static特征)。需要注意的是,您必须确保线程在生命周期到期之前退出 - 否则您将引用无效的内存!

使用mem::transmute可以忽略这一点,但代价是当内存不再有效时程序崩溃。

答案 1 :(得分:2)

您的案例中的解决方案是使用for关键字,该关键字允许您为输入参数指定“匿名”生命周期。

#![feature(box_syntax)]
#![feature(std_misc)]

use std::thunk::Invoke;

struct Type;

enum Message {
    Handle(Box<for <'r> Invoke<(&'r Type)> + Send>)
}

pub fn main() {
    let a = Type;
    let handler = Message::Handle(box move |_: &Type| {});
    match handler {
        Message::Handle(f) => f.invoke(&a)
    }
}

playpen link

此外,我认为使用transmute()技巧时的主要问题是,如果您的函数正在返回或存储对输入结构的一部分的引用:编译器会为此引用分配'静态生存期,但显然这是一个错误,可能会导致内存损坏。

您可以在this blog post中找到有关闭包的更多信息以及当前需要Invoke特征的原因(注意:显式指定闭包类型的部分现已过时,编译器现在推断它)。

答案 2 :(得分:0)

简单的答案是,使用:

Handle(Box<FnMut(&Type) + Send>)

更复杂的答案是,根据https://github.com/rust-lang/rust/blob/master/src/test/run-pass/unboxed-closures-prelude.rs,您无法调用Box<FnOnce(...)>,只能调用大小F,其中F:FnOnce&lt; ...&gt;

......不知道为什么,但我推测它是因为它随着特质消耗自我而移动了价值;很奇怪。