web-dev-qa-db-ja.com

ネストされた配列インデックスで「変更可能としても借りられるため、不変として借りることができない」とはどういう意味ですか?

この場合のエラーの意味:

_fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    v[v[1]] = 999;
}
_
_error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:3:7
  |
3 |     v[v[1]] = 999;
  |     --^----
  |     | |
  |     | immutable borrow occurs here
  |     mutable borrow occurs here
  |     mutable borrow later used here
_

索引付けはIndexIndexMut特性を介して実装され、_v[1]_は*v.index(1)の構文糖であることがわかりました。この知識を備えて、私は次のコードを実行しようとしました:

_use std::ops::{Index, IndexMut};

fn main() {
    let mut v: Vec<usize> = vec![1, 2, 3, 4, 5];
    *v.index_mut(*v.index(1)) = 999;
}
_

驚いたことに、これは完璧に機能します!最初のスニペットが機能しないのに、2番目のスニペットは機能するのはなぜですか?私がドキュメントを理解する方法では、それらは同等であるはずですが、明らかにそうではありません。

16
Lucas Boucke

脱糖バージョンは、お持ちのバージョンとは少し異なります。この線

_v[v[1]] = 999;
_

実際にdesugars

_*IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;
_

これにより同じエラーメッセージが表示されますが、注釈は何が起こっているかについてのヒントを提供します。

_error[E0502]: cannot borrow `v` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:48
  |
7 |     *IndexMut::index_mut(&mut v, *Index::index(&v, 1)) = 999;
  |      ------------------- ------                ^^ immutable borrow occurs here
  |      |                   |
  |      |                   mutable borrow occurs here
  |      mutable borrow later used by call
_

脱糖バージョンとの重要な違いは、評価順序です。関数呼び出しの引数は、実際に関数呼び出しを行う前に、リストされている順序で左から右に評価されます。この場合、これは最初に_&mut v_が評価され、可変的にvを借用することを意味します。次に、Index::index(&v, 1)を評価する必要がありますが、これは不可能です。vはすでに可変的に借用されています。最後に、コンパイラーは、index_mut()への関数呼び出しに変更可能な参照がまだ必要であることを示しているため、共有参照が試行されても変更可能な参照は引き続き有効です。

実際にコンパイルするバージョンでは、評価順序が少し異なります。

_*v.index_mut(*v.index(1)) = 999;
_

最初に、メソッド呼び出しの関数引数が左から右に評価されます。つまり、*v.index(1)が最初に評価されます。これによりusizeが発生し、vの一時的な共有ボローを再度解放できます。次に、index_mut()のレシーバーが評価されます。つまり、vは可変に借用されます。共有された借用は既に確定されており、式全体が借用チェッカーを通過するため、これは正常に機能します。

「非語彙的ライフタイム」の導入以来、コンパイルするバージョンはそうするだけであることに注意してください。 Rustの以前のバージョンでは、共有された借用は式の終わりまで存続し、同様のエラーが発生しました。

私の意見で最もクリーンな解決策は、一時変数を使用することです:

_let i = v[1];
v[i] = 999;
_
16
Sven Marnach