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
の署名を変更したいと思います。
後で&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;
}
}
}