首页 文章

返回具有可变环境的闭包

提问于
浏览
6

我正在尝试构建一个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
    }
}

那么执行总是返回 3acc 的初始值在闭包输入时总是 0 .

这种语义差异看起来像一个bug . 但除此之外,为什么呼叫之间的环境重置?

这是用rustc 0.12.0运行的 .

1 回答

  • 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涵盖 .

相关问题