如何在 Rust 的结构中存储闭包?
Posted
技术标签:
【中文标题】如何在 Rust 的结构中存储闭包?【英文标题】:How do I store a closure in a struct in Rust? 【发布时间】:2015-03-06 02:02:02 【问题描述】:在 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
类型没有专门化
【问题讨论】:
【参考方案1】:为了演示目的,用更多代码补充现有答案:
开箱关闭
使用泛型类型:
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);
我创建的
Foo
对象的类型是什么?
这是一种无法命名的自动生成类型。
我也可以使用更慢的引用 [...] 因为 [...] 指针 deref [...] 没有专门化
也许,但调用者会更容易。
另见:
How do I call a function through a member variable? Returning a closure from a function How to return an anonymous type from a trait method without using Box? Closures as a type in a Rust struct Types of unboxed closures being unique to each Why does passing a closure to function which accepts a function pointer not work? What does "dyn" mean in a type?【讨论】:
这些选择中是否有任何已知比其他选择更快? @DanielV 是的,也不是。如果保证其中之一普遍优于其他,我们就不需要其余的了。未装箱的闭包可能是默认选择,并且可能是性能最高的,但其他闭包可以生成更小的代码,这可能会导致它们的性能更高。 rust 确实需要 std::function 之类的东西,用于任何可调用的 c++ 统一容器。 盒装特征中的“+ 'static”是什么意思 @AliSomay 不捕获它,不,但在某些情况下,您可以将它作为参数传递给闭包本身。见How to use struct self in member method closure; How can I modify self in a closure called from a member function?【参考方案2】:对于您在第三个代码 sn-p 中使用的类型,没有一个;闭包类型是匿名的,不能直接命名。相反,你会写:
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
s 是堆分配的,相当于Box<dyn FnOnce(usize) -> usize>
(你只能调用一次proc
)。
【讨论】:
是否有可能创建像compose
或apply
这样的函数,它们通常适用于Fn
、FnMut
和FnOnce
3 个不同的特征?我发现自己必须创建 3 个不同版本的 compose
或 apply
来满足闭包的 3 个特征。
@CMCDragonkai 如果您需要更多详细信息,您应该提出一个新问题,但不,我不相信它是。但是请记住,所有Fn
s 都可以用作FnMut
s,所有Fn
s 和FnMut
s 都可以用作FnOnce
s。以上是关于如何在 Rust 的结构中存储闭包?的主要内容,如果未能解决你的问题,请参考以下文章
Rust闭包和Haskell lambda有什么区别? [关闭]
Rust 闭包和 Haskell lambda 有啥区别? [关闭]