闭包的状态中有一些数据,但是如何使其可变?例如,我想要一个每次都返回递增值的计数器闭包,但它不起作用。我如何让它工作?
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]
.
正如错误消息所说:
无法在
Fn
闭包中分配给捕获的外部变量
相反,您需要一个FnMut
关闭:
// In modern Rust, prefer returning `impl FnMut() -> i32`
fn counter() -> Box<dyn 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
的行为吗?
也许闭包何时实现 Fn、FnMut 和 FnOnce?可以帮助提供一些背景信息。这是我直觉的一个方面,但还没有弄清楚如何最好地沟通。Finding Closure in Rust 中的这一部分似乎也相关:
在高层次上,
self
为实施者(即用户定义的用于实现特征的类型)提供了最大的灵活性,其次是&mut self
和&self
最不灵活的。相反,&self
为特征的使用者(即具有受特征限制的泛型的函数)提供了最大的灵活性,self
最小。
简而言之,该特征定义表明,任何Fn
闭包都可以在预期FnMut
闭包的地方使用。这是有道理的,因为我们可以忽略FnMut
可用的可变性。你不能走另一条路 - 你不能将不可变的引用变成可变的引用。