如何用未装箱的闭包替换 proc?

How to replace proc with unboxed closures?

我在更换 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)
    }
}

简单的答案是,使用:

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

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

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

使用 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)

In order to be able to send the enum to another thread I have to use 'static and later mem::transmute to cast the lifetime.

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

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

您的解决方案是使用 for 关键字,它允许您为输入参数指定 "anonymous" 生命周期。

#![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() 技巧时的主要问题是,如果您的函数返回或存储对输入结构的一部分的引用:编译器会为此分配 'static 生命周期引用,而这显然是一个错误,并可能导致内存损坏。

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