"cannot move a value of type FnOnce" 移动盒装函数时

"cannot move a value of type FnOnce" when moving a boxed function

我正在尝试用 Rust 进行一些高阶编程,但我在处理闭包方面遇到了一些困难。这是一个代码片段,说明了我遇到的问题之一:

pub enum Foo {
    Bar(Box<FnOnce(i32)>),
}

pub fn app(i: i32, arg: Foo) {
    match arg {
        Foo::Bar(f) => f(i),
    }
}

当我编译这段代码时,我收到以下错误消息:

error[E0161]: cannot move a value of type std::ops::FnOnce(i32) + 'static: the size of std::ops::FnOnce(i32) + 'static cannot be statically determined
 --> src/main.rs:7:24
  |
7 |         Foo::Bar(f) => f(i),
  |                        ^

由于我将函数放在 Box 中,我原以为这样可以解决编译器不知道大小的问题。我怎样才能编译上面的程序?

这是 FnOnce 特征的定义(稍微简化了一点):

pub trait FnOnce<Args> {
    type Output;

    fn call_once(self, args: Args) -> Self::Output;
}

要调用 FnOnce 闭包,您需要能够将闭包值本身移动到调用中。请注意 self 必须是 actual 闭包类型; Box<dyn FnOnce> 是完全不同的类型。

Rust 1.35

Box<dyn FnOnce> 现在可以调用了;您的原始代码按原样工作。

以前的版本

标准库中一种类型来解决这种情况:FnBox可惜,不稳定

您的替代选择是:

  • 重构代码,以便保留 实际 闭包类型而不是 Box<FnOnce>
  • 改用Box<FnMut>
  • 等待 FnBox 稳定。
  • 切换到夜间编译器。

FnBox 不太可能变得稳定,但目前您可以将 F: FnOnce(...) -> ... 包装在 Option<F> 中,将其绑定在可变闭包中,然后解包并在内部调用它(所以如果它被调用不止一次,它就会恐慌);生成的闭包可以装箱为 Box<FnMut(...) -> ...>,您可能希望以某种方式包装它以确保它只被使用 ("called") 一次。

查看(我的)boxfnonce 箱子。