将修改其环境的闭包传递给Rust中的函数

时间:2016-07-30 19:27:24

标签: closures rust immutability traits

我有一个捕获并修改其环境的闭包。我想将此闭包传递给接受闭包的函数:

fn main() {
    let mut integer = 5;
    let mut closure_variable = || -> i32 {
        integer += 1;
        integer
    };
    execute_closure(&mut closure_variable);
}

fn execute_closure(closure_argument: &mut Fn() -> i32) {
    let result = closure_argument();
    println!("Result of closure: {}", result);
}

因为闭包修改了它的环境,所以失败了:

error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnMut`
 --> src/main.rs:3:32
  |
3 |       let mut closure_variable = || -> i32 {
  |  ________________________________^
4 | |         integer += 1;
5 | |         integer
6 | |     };
  | |_____^
7 |       execute_closure(&mut closure_variable);
  |                       --------------------- the requirement to implement `Fn` derives from here
  |
note: closure is `FnMut` because it mutates the variable `integer` here
 --> src/main.rs:4:9
  |
4 |         integer += 1;
  |         ^^^^^^^

据我所知When does a closure implement Fn, FnMut and FnOnce?,这意味着我的闭包实际上被扩展为实现特征FnMut的结构。这个特性是可变的,这意味着调用该函数会改变(隐式)对象。我认为这是正确的,因为在调用integer之后应该修改变量execute_closure()

如何说服编译器这没关系,我真的想调用FnMut函数?或者在这个例子中我如何使用Rust有根本性的错误?

1 个答案:

答案 0 :(得分:3)

如果您可以更改接受闭包的功能......

接受FnMut而不是Fn

fn main() {
    let mut integer = 5;
    execute_closure(|| {
        integer += 1;
        integer
    });
}

fn execute_closure<F>(mut closure_argument: F)
where
    F: FnMut() -> i32,
{
    let result = closure_argument();
    println!("Result of closure: {}", result);
}

如果您无法更改接受闭包的功能......

使用CellRefCell等类型提供的内部可变性

use std::cell::Cell;

fn main() {
    let integer = Cell::new(5);
    execute_closure(|| {
        integer.set(integer.get() + 1);
        integer.get()
    });
}

fn execute_closure<F>(closure_argument: F)
where
    F: Fn() -> i32,
{
    let result = closure_argument();
    println!("Result of closure: {}", result);
}
  

或者在这个例子中我如何使用Rust会有什么根本的错误?

也许。类型为&mut Fn() -> i32的参数不能改变它已关闭的变量,因此错误消息对我有意义。

kind 类似于&mut &u8类型 - 您可以更改外部引用以指向另一个不可变引用,但是您不能忽略&#34 ;内在的不变性和改变数值。

除了:

原始代码使用动态分派,因为有一个提供间接的特征对象。在许多情况下,您会看到我在上面发布的这个版本,它使用静态调度,并且可以单态化。我也将闭包描述为正常的语法。

这是原始版本,只需进行足够的更改即可使用:

fn main() {
    let mut integer = 5;
    let mut closure_variable = || -> i32 {
        integer += 1;
        integer
    };
    execute_closure(&mut closure_variable);
}

fn execute_closure(closure_argument: &mut FnMut() -> i32) {
    let result = closure_argument();
    println!("Result of closure: {}", result);
}