Rustにおける借用(ボローイング)は、変数を一時的に「借りる」ためのメカニズムであり、参照を介して行われます。Rustは非可変な借用(&T)と可変な借用(&mut T)を区別します。非可変参照は同時にいくつでも存在できますが、可変参照は1つだけです。1つのオブジェクトに対して、可変参照と非可変参照が同時に存在することは許可されません。
このルールは、コンパイル時にデータレースが発生しないことを保証し、Rustを安全な並行プログラミングのための言語にしています。
例:
let mut value = 5; let r1 = &value; let r2 = &value; // let r3 = &mut value; // エラー:&がある間は&mutを作成できない println!("{} {}", r1, r2); // r3はr1/r2のスコープが終了するまで禁止される
質問: 同じスコープ内で1つの&mut参照といくつでも&参照を作成することはできますか?
典型的な誤った回答: はい、ただしライフタイムが重ならない場合に限ります。
正しい回答: 同じオブジェクトに対して可変参照と非可変参照が同時に存在することはできません(たとえそれらのアクセスがコード内で静的に重ならなくても)、1つが生きている限り、他は禁止されます。安全性はボローイングチェッカーによって検証されます。
例:
let mut x = 10; let y = &x; let z = &mut x; // エラー、yはまだスコープ内 println!("{}", y); // yは後で必要
逸話
大規模なデータ並列処理プロジェクトで、開発者はベクターへの非可変参照を使用することに決め、その後、ソート用に可変参照を取得しようとしました。コードはテストで動作しましたが、リファクタリング後にコンパイルできなくなりました。なぜなら、非可変参照のライフタイムが拡大したからです。
逸話
内部サービスで
&mutを使用して構造体を変更しながら、フィールドへの参照を保持して別のスレッドに送信しようとしました。借用規則が守られなかったため、データレースとクラッシュが発生しました。Rustはコンパイル時にこれを防ぎますが、unsafeブロック内のエラーではその保証が取り除かれました。
逸話
APIの不適切な文書化:ライブラリは構造体の異なるフィールドに対して同時に
&および&mutを受け入れましたが、エイリアシングにより不変性が破壊され、このライブラリを統合したサービスに難解なバグが発生しました。