rust 如何迭代函数向量并调用它们而不移动向量?

9jyewag0  于 2022-12-19  发布在  其他
关注(0)|答案(1)|浏览(123)

我有一个结构体,它包含一个函数向量和一个迭代每个函数并调用它的方法。

struct App {
    functions: Vec<Box<dyn FnOnce(i32) -> i32>> 
}

impl App {
    pub fn run(&mut self) {
        for function in &mut self.functions {
            println!("{}", (function)(1));
        }
    }
}

然而,我移动了向量,我还没有找到一个干净的方法来复制它。

cannot move out of `*function` which is behind a mutable reference
move occurs because `*function` has type `Box<dyn FnOnce(i32) -> i32>`, which does not 
implement the `Copy` trait (rustc E0507)

那么,有没有办法调用函数而不移动它,或者有没有一种干净的方法来复制它呢?

yh2wf1be

yh2wf1be1#

Rust中函数特性的构造方式在开始时有点混乱,但遵循相同的所有权模式。
有三种功能所有权类型:

  • FnOnce-类似于self。要求调用方拥有此函数对象,并且将使用此对象。这是调用方对函数所能做出的最强保证,并且每个函数都是FnOnce
  • FnMut-类似于&mut self。要求调用方持有对函数的可变引用。可以多次调用。所有可能有副作用的函数至少需要FnMut所有权。每个可以通过FnMut执行的函数也与FnOnce兼容,因为FnOnce具有更强的所有权保证。
  • Fn-类似于&self。允许调用者不可变地引用此对象,这意味着可以同时从多个点引用和调用此对象(重要:这里不谈多线程,多线程还需要Sync).这是所有权要求最弱的一个,允许所有者通过这个调用的函数通常没有副作用,只是从一个输入计算一个输出.所有允许通过Fn自动调用的函数也都可以通过FnOnceFnMut调用.因为这能提供更强的所有权保障。

如果不考虑这个问题,在您的例子中,您的函数是FnOnce,这意味着它们需要拥有,调用它们将消耗它们,特别是最后一部分,这是您在这里得到错误的原因。
你在run方法中拥有self的方式太弱了。你不能在你不拥有的self对象上调用需要拥有权的FnOnce&mut self对象只允许你调用FnMut成员。
因此,有两种方法可以修复代码:

  • self参数的类型改为owned,但是要注意,这会使run成为一个消耗函数,最终会破坏self
  • 将函数对象的类型改为FnMut,当然只有当函数允许通过FnMut调用时才可以这样做。

以下是将FnOnce更改为Fn后的外观示例:

struct App {
    functions: Vec<Box<dyn Fn(i32) -> i32>>,
}

impl App {
    pub fn run(&self) {
        for function in &self.functions {
            println!("{}", (function)(1));
        }
    }
}

fn main() {
    let app = App {
        functions: vec![Box::new(|x| 2 * x), Box::new(|x| 3 * x)],
    };

    app.run();
    app.run();
}
2
3

注意,这里的闭包|x| 2 * xFn兼容,因为它没有任何副作用,这意味着self变量现在可以是一个不可变的引用,因为调用这些函数可以保证不改变self
我们还可以多次调用app.run()app不必是可变的。
下面是一个需要FnMut的函数示例:
一个二个一个一个
这个函数现在有一个副作用--每次调用它都会改变sum的值,因此它不再与Fn兼容,在前面的代码中使用它会导致错误。
我们的函数对象现在必须是FnMut,我们的self必须变成&mut self,我们的app对象必须是可变的。
但是,我们仍然能够多次调用app.run()
现在,让我们看看使用FnOnce的必要条件:

struct App {
    functions: Vec<Box<dyn FnOnce(i32) -> i32>>,
}

impl App {
    pub fn run(self) {
        for function in self.functions {
            println!("{}", (function)(1));
        }
    }
}

fn main() {
    let add = {
        let s = String::new();
        move |x| {
            drop(s);
            x
        }
    };

    let app = App {
        functions: vec![Box::new(add)],
    };

    app.run();
}

这个闭包是FnOnce的原因是因为drop,它在被调用时会破坏s变量,并且由于显而易见的原因,这种情况只会发生一次。
因此,要将其存储到App中,App必须将其函数对象类型更改为FnOnce,而且,run()方法中的self现在也需要作为拥有对象,因为在此过程中会破坏它的一部分。
这意味着我们只能调用app.run()一次,也不需要把app标记为mut,因为它在这个过程中不会发生变异,它会被完全消耗掉,所以我们不需要担心变异性,它在之后就不能被访问了。
我们现在只能调用app.run()一次是有意义的,因为它包含的函数也只能调用一次。
我希望这能帮助你更好地理解你的处境。

相关问题