您可以使用impl Fn接受任意大小的闭包作为参数吗?

时间:2018-08-09 15:58:51

标签: rust closures

我有一个History类型,其中包含一个初始状态,一个当前状态和一个闭包列表,其中每个更改都需要从初始状态计算当前状态。这些是通过.apply(...)方法应用的,该方法采用盒装闭包,使用它来修改当前状态,并将其添加到列表中。因为我希望它们可以确定地重用,所以它们是Fn,而不是FnMutFnOnce

struct History<State: Clone> {
    initial: State,
    current: State,
    updates: Vec<Box<dyn Fn(&mut State)>>,
}

impl<State: Clone> History<State> {
    fn apply(&mut self, update: Box<dyn Fn(&mut State)>) {
        update(&mut self.current);
        self.updates.push(update);
    }
}

我目前将闭包作为Box<dyn Fn(&mut State)>,并且工作正常:

fn main() {
    let mut history = History::<usize> {
        initial: 0,
        current: 0,
        updates: vec![],
    };

    let delta = 10;
    history.apply(Box::new(move |mut value| *value += delta));

    println!("{}", history.current);
}
10

这让我开始思考是否可以通过使用impl Trait而不是dyn Trait来接受任意 unboxed 闭包的方法。在这种情况下,我们的方法可以将闭包本身装箱,因此调用站点将变为:

    history.apply(move |mut value| *value += delta);

(请回答一个问题,即使在这种情况下这是个坏主意,是否也有可能。)

我正在想象每个闭包站点都像一个独特的数据类型,每次使用时都会用封闭的值实例化,因此impl Trait可以像对待每种显式类型一样专门针对每个隐式闭包使用该方法。但是我不确定Rust是否真的可以那样工作。

但是,当我尝试对代码进行更改时,出现了新的生命周期错误:

    fn apply(&mut self, update: impl Fn(&mut State)) {
        update(&mut self.current);
        self.updates.push(Box::new(update));
    }
error[E0310]: the parameter type `impl Fn(&mut State)` may not live long enough
  --> src/main.rs:10:27
   |
10 |         self.updates.push(Box::new(update));
   |                           ^^^^^^^^^^^^^^^^
   |
note: ...so that the type `impl Fn(&mut State)` will meet its required lifetime bounds
  --> src/main.rs:10:27
   |
10 |         self.updates.push(Box::new(update));
   |                           ^^^^^^^^^^^^^^^^

这使我感到困惑。我不确定哪里有可能会变坏的参考。

在我的脑海中,整个闭合状态现在通过apply参数移到impl Fn中,然后移到Box拥有的self中。但是它抱怨我不能将内容移动到盒子中,因为我有一个潜在的过时引用,而不仅仅是拥有的数据?我在哪里借东西?为什么当我直接在main中而不是在apply中将封闭框装箱时,为什么没有发生这种情况?


是否可以使用impl Fn接受(任意大小的)闭包作为参数?如果可以,怎么办?

1 个答案:

答案 0 :(得分:3)

  

您可以使用impl Fn接受任意大小的闭包作为参数吗?

是的。

参数位置的

impl trait与泛型完全相同。这些是相同的:

fn foo1(_: impl Fn(u8) -> i8) {}
fn foo2<F>(_: F)
where
    F: Fn(u8) -> i8,
{}

实际上,这是通常首选的方式来接受闭包(或许多其他特征实现),因为它允许编译器对结果进行变形,并避免任何不必要的间接调用


编译代码时会显示以下帮助文本(which currently has some rendering glitches):

help: consider adding an explicit lifetime bound `impl Fn(&mut State): 'static`...
   |
8  |     fn apply(&mut self, update: impl Fn(&mut State): 'static +  {
   |                                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

要说的是添加+ 'static

fn apply(&mut self, update: impl Fn(&mut State) + 'static)

这有效。

另请参阅: