如何在 Rust 的结构中存储闭包?
How do I store a closure in a struct in Rust?
在 Rust 1.0 之前,我可以使用这种过时的闭包语法编写结构:
struct Foo {
pub foo: |usize| -> usize,
}
现在我可以做类似的事情:
struct Foo<F: FnMut(usize) -> usize> {
pub foo: F,
}
但是我创建的 Foo
对象的类型是什么?
let foo: Foo<???> = Foo { foo: |x| x + 1 };
我也可以参考一下:
struct Foo<'a> {
pub foo: &'a mut FnMut(usize) -> usize,
}
我认为这比较慢,因为
- 指针取消引用
- 实际上最终使用的
FnMut
类型没有专门化
对于您在第三个代码片段中使用的类型,没有;闭包类型是匿名的,不能直接命名。相反,你会写:
let foo = Foo { foo: |x| x + 1 };
如果您在 需要 指定您想要 Foo
的上下文中编写代码,您可以这样写:
let foo: Foo<_> = Foo { foo: |x| x + 1 };
_
告诉类型系统为您推断实际的泛型类型。
关于使用哪个的一般经验法则,按降序排列:
- 通用参数:
struct Foo<F: FnMut(usize) -> usize>
。这是最有效的,但它确实意味着特定的 Foo
实例只能存储 一个 闭包,因为每个闭包都有不同的具体类型。
- 特征引用:
&'a mut dyn FnMut(usize) -> usize
。有一个指针间接,但现在您可以存储对任何具有兼容调用签名的闭包的引用。
- 盒装封口:
Box<dyn FnMut(usize) -> usize>
。这涉及在堆上分配闭包,但您不必担心生命周期。与引用一样,您可以存储任何具有兼容签名的闭包。
Rust 1.0 之前
使用 ||
语法的闭包是对存储在堆栈中的闭包的引用,使它们等同于 &'a mut FnMut(usize) -> usize
。旧式 proc
是堆分配的,相当于 Box<dyn FnOnce(usize) -> usize>
(您只能调用一次 proc
)。
使用更多代码补充 用于演示目的:
开箱封口
使用通用类型:
struct Foo<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo<F>
where
F: Fn(usize) -> usize,
{
fn new(foo: F) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
盒装特征对象
struct Foo {
pub foo: Box<dyn Fn(usize) -> usize>,
}
impl Foo {
fn new(foo: impl Fn(usize) -> usize + 'static) -> Self {
Self { foo: Box::new(foo) }
}
}
fn main() {
let foo = Foo {
foo: Box::new(|a| a + 1),
};
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
特征对象引用
struct Foo<'a> {
pub foo: &'a dyn Fn(usize) -> usize,
}
impl<'a> Foo<'a> {
fn new(foo: &'a dyn Fn(usize) -> usize) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: &|a| a + 1 };
(foo.foo)(42);
(Foo::new(&|a| a + 1).foo)(42);
}
函数指针
struct Foo {
pub foo: fn(usize) -> usize,
}
impl Foo {
fn new(foo: fn(usize) -> usize) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
what's the type of a Foo
object I create?
这是一个无法命名的自动生成的类型。
I could also use a reference [...] slower because [...] the pointer deref [...] no specialization
也许可以,但调用者会容易得多。
另请参阅:
- Returning a closure from a function
在 Rust 1.0 之前,我可以使用这种过时的闭包语法编写结构:
struct Foo {
pub foo: |usize| -> usize,
}
现在我可以做类似的事情:
struct Foo<F: FnMut(usize) -> usize> {
pub foo: F,
}
但是我创建的 Foo
对象的类型是什么?
let foo: Foo<???> = Foo { foo: |x| x + 1 };
我也可以参考一下:
struct Foo<'a> {
pub foo: &'a mut FnMut(usize) -> usize,
}
我认为这比较慢,因为
- 指针取消引用
- 实际上最终使用的
FnMut
类型没有专门化
对于您在第三个代码片段中使用的类型,没有;闭包类型是匿名的,不能直接命名。相反,你会写:
let foo = Foo { foo: |x| x + 1 };
如果您在 需要 指定您想要 Foo
的上下文中编写代码,您可以这样写:
let foo: Foo<_> = Foo { foo: |x| x + 1 };
_
告诉类型系统为您推断实际的泛型类型。
关于使用哪个的一般经验法则,按降序排列:
- 通用参数:
struct Foo<F: FnMut(usize) -> usize>
。这是最有效的,但它确实意味着特定的Foo
实例只能存储 一个 闭包,因为每个闭包都有不同的具体类型。 - 特征引用:
&'a mut dyn FnMut(usize) -> usize
。有一个指针间接,但现在您可以存储对任何具有兼容调用签名的闭包的引用。 - 盒装封口:
Box<dyn FnMut(usize) -> usize>
。这涉及在堆上分配闭包,但您不必担心生命周期。与引用一样,您可以存储任何具有兼容签名的闭包。
Rust 1.0 之前
使用 ||
语法的闭包是对存储在堆栈中的闭包的引用,使它们等同于 &'a mut FnMut(usize) -> usize
。旧式 proc
是堆分配的,相当于 Box<dyn FnOnce(usize) -> usize>
(您只能调用一次 proc
)。
使用更多代码补充
开箱封口
使用通用类型:
struct Foo<F>
where
F: Fn(usize) -> usize,
{
pub foo: F,
}
impl<F> Foo<F>
where
F: Fn(usize) -> usize,
{
fn new(foo: F) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
盒装特征对象
struct Foo {
pub foo: Box<dyn Fn(usize) -> usize>,
}
impl Foo {
fn new(foo: impl Fn(usize) -> usize + 'static) -> Self {
Self { foo: Box::new(foo) }
}
}
fn main() {
let foo = Foo {
foo: Box::new(|a| a + 1),
};
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
特征对象引用
struct Foo<'a> {
pub foo: &'a dyn Fn(usize) -> usize,
}
impl<'a> Foo<'a> {
fn new(foo: &'a dyn Fn(usize) -> usize) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: &|a| a + 1 };
(foo.foo)(42);
(Foo::new(&|a| a + 1).foo)(42);
}
函数指针
struct Foo {
pub foo: fn(usize) -> usize,
}
impl Foo {
fn new(foo: fn(usize) -> usize) -> Self {
Self { foo }
}
}
fn main() {
let foo = Foo { foo: |a| a + 1 };
(foo.foo)(42);
(Foo::new(|a| a + 1).foo)(42);
}
what's the type of a
Foo
object I create?
这是一个无法命名的自动生成的类型。
I could also use a reference [...] slower because [...] the pointer deref [...] no specialization
也许可以,但调用者会容易得多。
另请参阅:
- Returning a closure from a function