このページの目次
参照と借用
Rustの借用チェッカーが認めるルールは1つだけ:同時に、可変参照1つ、または不変参照複数、どちらか一方のみ(エイリアシングXOR変更)。NLLはチェック粒度を字句スコープから実際の使用期間へ細分化し、reborrowによりネストされた借用に明示的な注釈が不要になる——これら3つが組み合わさり、Rustの「GCなしのメモリ安全性」を構成している。
& vs &mut
let mut s = Stringfrom;
let r1 = &s; // 不変借用 (共有参照)
let r2 = &s; // 複数の不変借用が可能
println!; // OK
let r3 = &mut s; // 可変借用: sがmutとして宣言されている必要がある
// let r4 = &s; // ERROR: &mutが既に存在するため、&は作成できない
r3.push;
核心ルール——エイリアシングXOR変更:同時に、複数の共有参照(読み取り専用)またはちょうど1つの可変参照(書き込み可能)のどちらか一方のみが存在し、両者は共存できない。このルールはデータ競合とイテレータの無効化を防ぐ。
Borrow Checkerの動作
コンパイラはMIR(Mid-level IR)上で借用チェックを行う。各借用が作成される際に、どの変数を借用したか、sharedかmutableか、いつまで生存するかを記録する。
let mut v = vec!;
let first = &v; // vの不変借用開始
v.push; // ERROR: vの可変借用 — firstはまだ生存中
println!; // firstの最終使用箇所
// firstの借用はここで終了
エラーの原因:v.push(4) は &mut v を必要とするが、first はまだスコープ内にあり &v を保持している。borrow checkerは &v と &mut v の同時存在を許可できない——push が v[0] に影響を与えないことが分かっていても、borrow checkerはそれを知らない。
Non-Lexical Lifetimes (NLL、2018エディション)
NLL以前は、借用の生存期間はスコープによって決定されていた(字句的——変数は宣言から最終使用箇所までのブロック全体)。NLLでは最終使用箇所によって決定されるように変更された:
let mut v = vec!;
let first = &v;
println!; // firstの最終使用箇所
// firstの借用はここで終了(スコープの末尾ではなく!)
v.push; // OK: firstはもはや生存していないため、借用は終了している
// NLL以前では、これはコンパイルできなかった
Reborrow
&mut T を関数に渡す際、借用は「reborrow」される:
let mut v = vec!;
push; // reborrow: &mut *v → pushの &mut v
// pushの終了後、reborrowも終了 → vの所有権が回復
// 等価なコード:
// let reborrow = &mut *v;
// push(reborrow);
// drop(reborrow); ← reborrowはここでdropされ、vが回復
Referenceのメモリレイアウト
&T と &mut T は実行時に通常のポインタ(64ビット環境で8バイト)である。Borrow checkerのすべてのチェックはコンパイル時に行われ、実行時のオーバーヘッドはゼロ。
let x = 42;
let r = &x; // アセンブリ: lea rax, [rsp+4]; mov [rsp+8], rax
Fat pointer: &[T] と &dyn Trait は16バイト——長さまたはvtableポインタが追加で格納される。
一般的なトラップ
1. 不変借用中に元の値を変更できない
let mut v = vec!;
let r = &v;
v.clear; // ERROR: &v(不変)がアクティブなため、&mut vは作成できない
2. イテレータの無効化はborrow checkerによって防止される
let mut v = vec!;
for item in &v
3. 関数が参照を返す場合——明示的なlifetimeが必要
// 返される参照は s を借用する → コンパイラは &str が s より長く生存しないことを保証
参考
- Rust Book: 第4章 4.2節 — 参照と借用
- NLL RFC: RFC 2094
- Polonius: github.com/rust-lang/polonius (次世代借用チェッカー)
Keywords: borrow checker, shared reference, mutable reference, NLL, reborrow, aliasing XOR mutation, fat pointer