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

Shc*_*ova 7 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()]);
}
Run Code Online (Sandbox Code Playgroud)

错误(和警告)我得到:

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
    })
Run Code Online (Sandbox Code Playgroud)

我希望它输出类似的东西[1, 2, 3, 1, 2, 4].

She*_*ter 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);
}
Run Code Online (Sandbox Code Playgroud)

正如FnMut文档所说:

采用可变接收器的呼叫运算符的一个版本.

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

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


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

也许封闭什么时候实现Fn,FnMut和FnOnce?可以帮助提供一些背景信息.这是我直观的一个方面,但还没有弄清楚如何最好的沟通.Rust中的Finding Closure的这一部分似乎也很相关:

在高层次上self,实施者(即用户定义的用于实现特征的类型)具有最大的灵活性,具有&mut self下一个和&self最不灵活的特性.相反,&self给予消费者特征(即具有特征限制的仿制药的功能)最灵活,self最少.

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