如何在Rust中存储闭包?

bfo*_*ops 25 closures rust

我曾经有类似的东西:

struct Foo {
    pub foo: |usize| -> usize,
}
Run Code Online (Sandbox Code Playgroud)

现在闭包语法已经过时了.我可以这样做:

struct Foo<F: FnMut(usize) -> usize> {
    pub foo: F,
}
Run Code Online (Sandbox Code Playgroud)

但那么Foo我创建的对象的类型是什么?

let foo: Foo<???> = Foo { foo: |x| x + 1 };
Run Code Online (Sandbox Code Playgroud)

我也可以使用参考:

struct Foo<'a> {
    pub foo: &'a mut FnMut(usize) -> usize,
}
Run Code Online (Sandbox Code Playgroud)

但我觉得它的速度较慢,因为a)指针deref,以及b)现在对于FnMut实际最终被使用的类型没有专门化.

DK.*_*DK. 36

这是错误的:直接的等价物是||.这实际上是旧语法的实际含义.

更正:正如dbaupp所指出的,这是不正确的.使用该&'a mut FnMut(usize) -> usize语法的旧式闭包是对存储在堆栈中的闭包的引用,使它们等效于proc.它是Box<dyn FnOnce(usize) -> usize>堆分配的,相当于proc(你只能调用Foo一次).我为这个错误道歉.

至于你在第三个代码片段中使用的类型,没有一个; 闭包类型是匿名的,不能直接命名.相反,你会写:

let foo = Foo { foo: |x| x + 1 };
Run Code Online (Sandbox Code Playgroud)

如果您在需要指定所需内容的上下文中编写代码,则需要_编写:

let foo: Foo<_> = Foo { foo: |x| x + 1 };
Run Code Online (Sandbox Code Playgroud)

struct Foo<F: FnMut(usize) -> usize>告诉类型系统来推断实际的泛型类型适合你.

关于使用哪个的一般经验法则,按降序排列:

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


She*_*ter 16

为了演示的目的,用一些其他代码补充现有答案:

无盒盖

使用通用类型:

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);
}
Run Code Online (Sandbox Code Playgroud)

装箱特征对象

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);
}
Run Code Online (Sandbox Code Playgroud)

特性对象参考

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);
}
Run Code Online (Sandbox Code Playgroud)

功能指针

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);
}
Run Code Online (Sandbox Code Playgroud)

Foo我创建的对象的类型是什么?

这是一种无法命名的自动生成的类型。

我也可以使用一个较慢的引用,因为该指针不专门化。

也许可以,但是在调用方上会容易得多。

也可以看看:

  • @DanielV 是的,也不是。如果其中一个保证普遍优于其他,我们就不需要其余的了。未装箱的闭包可能是默认选择,并且可能是性能最高的,但其他闭包可以生成更小的代码,这可能会导致它们的性能更高。 (2认同)
  • @AliSomay 不要捕获它,不,但在某些情况下你可以将它作为参数传递给闭包本身。参见[如何在成员方法闭包中使用struct self](/sf/ask/3410248341/);[如何在从成员函数调用的闭包中修改 self ?](/sf/ask/2001816631/) (2认同)
  • 每种方法的优点和缺点是什么? (2认同)

归档时间:

查看次数:

11642 次

最近记录:

6 年,11 月 前