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

标签 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 .在这种情况下,我们的方法可以将闭包本身装箱,因此调用站点将变为:

    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接受一个(任意大小的)闭包作为参数?如果是,怎么办?

最佳答案

Can you use impl Fn to accept arbitrarily-sized closures as arguments?

是的。

参数位置中的

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)

这有效。

另见:

关于rust - 您可以使用 impl Fn 接受任意大小的闭包作为参数吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51771465/

相关文章:

rust - 如何优化从具有已知偏移量和大小的文件中读取 UTF-8 字符串?

java - 将对集合中的元素运行方法的所有结果添加到另一个集合

rust - 将 GtkSourceView 与 Rust 结合使用

rust - 如何从默认方法返回特殊化的关联类型?

rust - 使用泛型时, `From` 的实现如何冲突?

rust - 使用返回与一个参数具有相同生命周期的关联类型的函数定义特征

javascript - 'new function(){...}' 上的 JSLint 错误,但为什么呢?如果我想要匿名闭包,有没有更好的方法?

javascript - 无法访问关闭的 jQuery 对象的方法

javascript - 将库转换为 JavaScript 闭包

javascript - 不完全理解这个闭包是如何工作的