如何将可变状态移动到闭包中?

时间:2016-06-21 15:56:20

标签: rust

闭包在其状态中有一些数据,但我如何使其变为可变?例如,我想要一个计数器闭包,每次都返回递增的值,但它不起作用。我如何使它工作?

fn counter() -> Box<Fn() -> i32> {
    let mut c: i32 = 0;
    Box::new(move || {
        c += 1;
        c
    })
}

fn main() {
    let mut a = counter();
    let mut b = counter();
    println!("{:?}", [a(), a(), a(), b(), b(), a()]);
}

错误(和警告)我得到了:

error: cannot assign to captured outer variable in an `Fn` closure
        c += 1;
        ^~~~~~
help: consider changing this closure to take self by mutable reference
    Box::new(move || {
        c += 1;
        c
    })

我希望它输出类似[1, 2, 3, 1, 2, 4]的内容。

1 个答案:

答案 0 :(得分:10)

正如错误消息所示:

  

无法在Fn闭包

中分配给捕获的外部变量

相反,你想要一个FnMut闭包:

fn counter() -> Box<FnMut() -> i32> {
    let mut c = 0;
    Box::new(move || {
        c += 1;
        c
    })
}

fn main() {
    let mut a = counter();
    let mut b = counter();

    let result = [a(), a(), a(), b(), b(), a()];
    println!("{:?}", result);

    assert_eq!([1, 2, 3, 1, 2, 4], result);
}

正如FnMut文档所说:

  

一个带有可变接收器的调用操作符的版本。

这允许闭包改变包含的状态。

顺便说一句,不需要c的显式类型。

  

令我困惑的是pub trait Fn<Args>: FnMut<Args>。这不是说Fn(我使用过的)应该支持FnMut的行为吗?

或许When does a closure implement Fn, FnMut and FnOnce?可以帮助提供一些背景信息。这是我直观的一个方面,但还没有弄清楚如何最好的沟通。 Finding Closure in Rust的这一部分似乎也很相关:

  

在高级别self给予实施者(即用户定义以实现特征的类型)最大的灵活性,其次是&mut self&self最不灵活。相反,&self给予消费者特征(即具有特征限制的泛型的功能)最灵活,并且self最少。

简而言之,该特征定义表明任何FnMut闭包都可以用作Fn闭包。这是有道理的,因为我们可以简单地忽略可变性。你不能走另一条路 - 你不能将一个不可变的引用变成一个可变的引用。