web-dev-qa-db-ja.com

「その一時的な一時的な脱落したときに借用されている場合は借りる」があるため、別の方法でイテレータを使用できません。

2つの方法の構造があります。最初のメソッドはいくつかのフィールドを介してイテレータを返します。 2番目のメソッドは最初のものを呼び出し、そのフィールドを変更しようとします( 遊ぶ )。

struct Container {
    vec: Vec<f64>,
}

impl Container {
    fn large_positions<'a>(&'a self) -> impl Iterator<Item = usize> + 'a {
        self.vec
            .iter()
            .enumerate()
            .filter_map(|(i, &f)| if f > 3.14 { Some(i) } else { None })
    }

    fn negate_first_large_entry(&mut self) {
        if let Some(i) = self.large_positions().next() {
            self.vec[i] *= -1.0;
        }
    }
}

借りたチェッカーは以下のとおりです。

error[E0502]: cannot borrow `self.vec` as mutable because it is also borrowed as immutable
  --> src/lib.rs:15:13
   |
14 |         if let Some(i) = self.large_positions().next() {
   |                          ----------------------
   |                          |
   |                          immutable borrow occurs here
   |                          a temporary with access to the immutable borrow is created here ...
15 |             self.vec[i] *= -1.0;
   |             ^^^^^^^^ mutable borrow occurs here
16 |         }
17 |     }
   |     - ... and the immutable borrow might be used here, when that temporary is dropped and runs the destructor for type `impl Iterator`

手動でインラインでlarge_positionsの場合、デストラクタのことは本当の問題ではないようにコードがコンパイルされます。

ボローチェッカーはなぜインライン版に衝突がないことを知っているのですか?そして、これを非線形のバージョンの借りたチェッカーに説明できますか?

更新:次のわずかに異なるバージョンの2番目の関数は同じ動作を示します:( 遊び場

fn negate_first_large_entry_below_100(&mut self) {
    for i in self.large_positions() {
        if self.vec[i] < 100.0 {
            self.vec[i] *= -1.0;
            return;
        }
    }
}

ここで、returnステートメントは、self.vecの借用者がif分岐を入力するとすぐにリリースできることを借りたチェッカーに理解します。しかし、これはlarge_positionsがインライン化されている場合にのみ機能します。

理想的には、借りたチェッカーが依然としてこの巧妙な「早期解放」を実行できるように、large_positionsの署名を変更したいと思います。

2
wonce

後で&mutを取得するには、後で削除できる変数にイテレータを拡張できます。

    fn negate_first_large_entry(&mut self) {
        let mut iter = self.large_positions();
        if let Some(i) = iter.next() {
            drop(iter);
            self.vec[i] *= -1.0;
        }
    }

遊び場

2番目の方法では、同じトリックを実行するためのFORループの代わりにwhileループを使用する必要があります。

    fn negate_first_large_entry_below_100(&mut self) {
        let mut iter = self.large_positions();
        while let Some(i) = iter.next() {
            if self.vec[i] < 100.0 {
                drop(iter);
                self.vec[i] *= -1.0;
                return;
            }  
        }
    }

遊び場

2
Netwave