当前位置: 首页 > 知识库问答 >
问题:

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

公羊安怡
2023-03-14

我有一个 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

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

这让我想到,通过使用< code>impl Trait而不是< code>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));
   |                           ^^^^^^^^^^^^^^^^

这让我困惑。我不知道哪里有可能会变坏的参考文献。

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

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

共有1个答案

凌波峻
2023-03-14

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

是的。

参数位置中的 impl 特征与泛型完全相同。这些是相同的:

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

这实际上是接受闭包(或许多其他特征实现)的通常首选方法,因为它允许编译器对结果进行单态化并避免任何不必要的间接关系。

编译代码时有以下帮助文本(目前存在一些渲染问题):

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

它的意思是添加< code >‘static :

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

这有效。

另见:

  • 是什么使'impl Trait'作为参数“通用”和返回值“存在”?
  • 为什么使用加号运算符(迭代器
 类似资料:
  • 问题 你想构造一个可接受任意数量参数的函数。 解决方案 为了能让一个函数接受任意数量的位置参数,可以使用一个*参数。例如: def avg(first, *rest): return (first + sum(rest)) / (1 + len(rest)) # Sample use avg(1, 2) # 1.5 avg(1, 2, 3, 4) # 2.5 在这个例子中,rest是由所

  • 本文向大家介绍C++中可以接受任意多个参数的函数定义方法(详解),包括了C++中可以接受任意多个参数的函数定义方法(详解)的使用技巧和注意事项,需要的朋友参考一下 能够接受任意多个参数的函数,可以利用重载来实现。这种函数的执行过程类似于递归调用,所以必须要有递归终止条件。 执行后的结果如下: 以上就是小编为大家带来的C++中可以接受任意多个参数的函数定义方法(详解)全部内容了,希望大家多多支持呐喊

  • 我试图使用一组带有可变参数的模板类。我面前有几个选择,我可以选择。在声明或定义任何模板之前,我目前拥有这些原型:我熟悉模板,但在使用模板时没有太多可变类型的经验,因此语法有时会让我感到有点困惑。因为它们都是空的shell,所以它们当前正在编译。 我有一个用户终端类,它将根据使用意图使用这些类;在我使用适当的行为正确定义其他类之前,它目前是一个空壳: 上面显示的原型类的其余部分将从基类继承,这样上面

  • 我有一个函数,它有两个参数,最后一个参数是回调闭包: 我想让第二个回调闭包参数可选。我试图将上面的函数重新定义为: 我收到编译器错误: nil默认参数值不能转换为类型'

  • 闭包作为参数(Taking closures as arguments) 现在我们知道了闭包是 trait,我们已经知道了如何接受和返回闭包;就像任何其它的 trait! 这也意味着我们也可以选择静态或动态分发。首先,让我们写一个获取可调用结构的函数,调用它,然后返回结果: fn call_with_one<F>(some_closure: F) -> i32 where F : Fn(i

  • 不存在从“lambda[]void(GLFWwindow*window,int key,int scancode,int action,int mods)->void”到glfwkeyfun“的适当转换函数 我是漏掉了什么还是这段代码只是无效?