闭包保留局部变量的所有权的时间超过预期

时间:2017-07-26 14:48:14

标签: iterator rust closures borrow-checker

let array = [40];
let mut var = 60;

for element in array.iter().filter(|&x| {*x < var}) {
    var += 1; // Error
}

var += 1; // Fine again

对我来说,这段代码似乎完全合法,因为在我实际访问var之外时,闭包应该结束。

error[E0506]: cannot assign to `var` because it is borrowed
 --> src/main.rs:6:9
  |
5 |     for element in array.iter().filter(|&x| {*x < var}) {
  |                                        ---- borrow of `var` occurs here
6 |         var += 1; // Error
  |         ^^^^^^^^ assignment to borrowed `var` occurs here

为什么var在调用var += 1时仍然借用,即使闭包的范围已经结束了?到达var += 1需要结果。虽然可以在没有filter的情况下执行此类操作,但这会导致我的代码不那么清晰,所以我希望继续使用它。

2 个答案:

答案 0 :(得分:4)

  

关闭应该在我实际访问时结束

没有。 Iterators are lazy。这意味着这里的操作顺序是:

  1. 我们在组合迭代器上调用next
  2. 从数组中调用迭代器上的next。重复此步骤,直到条件通过。
  3. 使用值运行循环体。
  4. 重复整个过程。
  5. 您正在过滤器关闭内捕获val。您还尝试在循环中修改它。这意味着必须同时存在可变引用和不可变引用,这是不允许的。

    您可以使用Cell进行内部可变性:

    use std::cell::Cell;
    
    fn main() {
        let array = [40];
        let var = Cell::new(60);
    
        for element in array.iter().filter(|&&x| x < var.get()) {
            var.set(var.get() + 1);
        }
    
        let mut var = var.get();
        var += 1;
    }
    

答案 1 :(得分:2)

Shepmaster的答案是正确的(我不知道迭代器是 懒惰的),但是如果你不想要var检查(即在循环期间要更改的filter条件),您可以使用move closure

filter(move |&x| *x < var)

由于i32实现了Copy,因此只会为了关闭而复制var的值。