如何用未装箱的闭包替换 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);
}
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)
}
}
此外,我认为使用您的 transmute()
技巧时的主要问题是,如果您的函数返回或存储对输入结构的一部分的引用:编译器会为此分配 'static 生命周期引用,而这显然是一个错误,并可能导致内存损坏。
您可以在 this blog post 中找到有关闭包以及为什么当前需要 Invoke
特征的更多信息(注意:关于显式指定闭包类型的部分现在已经过时,编译器现在推断它).
我在更换 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);
}
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)
}
}
此外,我认为使用您的 transmute()
技巧时的主要问题是,如果您的函数返回或存储对输入结构的一部分的引用:编译器会为此分配 'static 生命周期引用,而这显然是一个错误,并可能导致内存损坏。
您可以在 this blog post 中找到有关闭包以及为什么当前需要 Invoke
特征的更多信息(注意:关于显式指定闭包类型的部分现在已经过时,编译器现在推断它).