如何在 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&lt;F: FnMut(usize) -&gt; usize&gt;。这是最有效的,但它确实意味着特定的 Foo 实例只能存储 一个 闭包,因为每个闭包都有不同的具体类型。 特征参考:&amp;'a mut dyn FnMut(usize) -&gt; usize。有一个指针间接,但现在您可以存储对任何具有兼容调用签名的闭包的引用。 盒装瓶盖:Box&lt;dyn FnMut(usize) -&gt; usize&gt;。这涉及在堆上分配闭包,但您不必担心生命周期。与引用一样,您可以存储任何具有兼容签名的闭包。

Rust 1.0 之前

使用|| 语法的闭包是对存储在堆栈中的闭包的引用,因此它们等同于&amp;'a mut FnMut(usize) -&gt; usize。旧式procs 是堆分配的,相当于Box&lt;dyn FnOnce(usize) -&gt; usize&gt;(你只能调用一次proc)。

【讨论】:

是否有可能创建像composeapply 这样的函数,它们通常适用于FnFnMutFnOnce 3 个不同的特征?我发现自己必须创建 3 个不同版本的 composeapply 来满足闭包的 3 个特征。 @CMCDragonkai 如果您需要更多详细信息,您应该提出一个新问题,但不,我不相信它是。但是请记住,所有Fns 都可以用作FnMuts,所有Fns 和FnMuts 都可以用作FnOnces。

以上是关于如何在 Rust 的结构中存储闭包?的主要内容,如果未能解决你的问题,请参考以下文章

Rust闭包和Haskell lambda有什么区别? [关闭]

Rust 闭包和 Haskell lambda 有啥区别? [关闭]

Rust:如何散列一个字符串?

我是如何提升 Rust 编译器的速度?

rust - 将闭包传递给特征方法: expected type parameter,发现闭包

你如何在结构之前的一个文档块中记录一个 Rust 结构/枚举?