我正在尝试构建一个Graham´s accumulator factory challenge的解决方案,它基本上需要一个函数来返回一个闭包,该闭包关闭一个可变数值变量,该变量通过参数接收初始值。每次调用此闭包都会将此捕获的变量增加一个值,该值是闭包的参数并返回累计值。
阅读closures RFC以及有关返回未装箱的闭包的一些问题(特别是this)。我终于可以提出一个编译的解决方案,但结果并不是我所期望的。
#![feature(unboxed_closures, unboxed_closure_sugar)]
fn accumulator_factory(n: f64) -> Box<|&mut: f64| -> f64> {
let mut acc = n;
box |&mut: i: f64| {
acc += i;
acc
}
}
fn main() {
let mut acc_cl = accumulator_factory(5f64);
println!("{}", acc_cl.call_mut((3f64,)));
println!("{}", acc_cl.call_mut((3f64,)));
}
AFAIK此闭包按值捕获acc
,生成的结构充当环境是可变的,acc_cl
应该在调用之间保留相同的环境实例。
但是在两种情况下打印结果都是6
,因此修改后的值似乎不会持续存在。更令人困惑的是如何计算这个结果。在每次执行闭包时,acc
的初始值为3
,即使n
在调用时为5
。
如果我将生成器修改为:
fn accumulator_factory(n: f64) -> Box<|&mut: f64| -> f64> {
println!("n {}", n);
let mut acc = n;
box |&mut: i: f64| {
acc += i;
acc
}
}
然后执行总是返回3
,并且在关闭条目时acc
的初始值始终为0
。
语义上的这种差异看起来像一个bug。但除此之外,为什么呼叫之间的环境重置?
这是用rustc 0.12.0运行的。
答案 0 :(得分:8)
最近关闭的捕获模式发生了变化。闭包是偏向于通过引用捕获所有内容,因为闭包的最常见用例是将它们传递给调用堆栈中的函数,并且通过引用捕获使得使用环境更自然。
有时通过引用捕获是有限的。例如,您无法从函数返回此类闭包,因为它们的环境与调用堆栈相关联。对于这样的闭包,您需要在闭包之前放置move
关键字:
fn accumulator_factory(n: f64) -> Box<FnMut(f64) -> f64> {
println!("n: {}", n);
let mut acc = n;
Box::new(move |i: f64| {
acc += i;
acc
})
}
fn main() {
let mut acc = accumulator_factory(10.0);
println!("{}", acc(12.0));
println!("{}", acc(12.0));
}
此程序按预期工作:
n: 10
22
34
this RFC涵盖了这两种闭包类型。