2017-07-26 18 views
2
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 += 1時,即使關閉的範圍應該已經結束了被var還是借來的?結果需要達到var += 1。雖然可以在沒有filter的情況下做這樣的事情,但這會導致我的代碼不太清晰,所以我想繼續使用它。

回答

4

封應該超過的時候我居然訪問

Iterators are lazy。這意味着這裏的操作順序是:

  1. 我們在組合迭代器上調用next
  2. 這在數組的迭代器上調用next。重複此步驟直到條件通過。
  3. 用該值運行循環體。
  4. 重複整個過程。

您正在捕獲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; 
} 
+0

因此,如果我正確地解釋了一切,使用filter()將創建一個包含閉包(並借用val)的新結構,直到它超出範圍,這種情況一旦迭代器被調用並返回None,循環結束,過濾器結構超出範圍,意味着它停止借用val,我可以再次變異它。如果這是真的,我現在終於明白迭代器是如何工作的。謝謝,我的英文不好,我正在寫我的手機。 – SleepingPanda

+1

@SleepingPanda是的。它創建一個['Filter'](https://doc.rust-lang。org/std/iter/struct.Filter.html)包含閉包的結構,閉包嘗試不斷地借用'var'。循環體試圖改變'var'。該結構的所有權被轉移到'for'循環中。 – Shepmaster

2

Shepmaster的答案是正確的(我不知道是迭代器是),但如果你不希望var檢查(即filter條件)的循環過程中改變,你可以使用一個move closure

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

由於i32工具Copyvar值將剛剛複製的閉合的目的。

+0

這改變了(大概需要)的邏輯。我假設過濾器應該每次迭代「動態」更新。 – Shepmaster

+0

@Shepmaster我的印象是,期望循環的主體執行時,過濾器已經完成了它的工作(因此問題是爲什麼借用內部仍然有效)。 – ljedrz

+0

我的實際代碼'var'不會改變,但它是一個相當大的結構,所以使用複製將是一個壞的舉動。一旦我有一段時間了,我將用拷貝來測試性能,因爲我只需要我的結構的一部分用於'filter()'。謝謝:) – SleepingPanda