項目15:借用チェッカを理解しよう
LT;DR
借用チェッカの出力するエラーに対処する方法はいくつかある
ブロック { ... } によるスコープを追加して、生存期間を縮小する
名前を持つローカル変数を導入することで、生存期間をスコープの末尾にまで拡大する
一時的に複数のローカル変数を追加することで、借用チェッカが指摘している箇所を特定する
スマートポインタ を用いると借用チェッカを回避できることがあるので、相互接続したデータ構造を作る際に有用 hr.icon
Rust の 参照 は 借用 されるため、参照されるアイテムの 生存期間 よりも長く持っておくことはできない 参照とアクセス制御
アイテム内にアクセスする方法には以下の 3 つがある
1. item(所有): 生成と読み込み、更新、ドロップ、ムーブ が可能 2. &item(不変参照): 読み込みのみ可能
3. &mut item(可変参照): 読み込みと更新が可能
ムーブは、(新しい場所での)生成と(古い場所での)ドロップと考えれば、所有でのみ可能なのが分かる
上記のようなアクセス制御を持つため、可変参照内の 古い値を取り出し、新しい値で置き換えることはできない
code:rs
pub fn replace(item: &mut Option<Item>, val: Item) -> Option<Item> {
let previous = *item; // ここで許可されていないムーブが発生するため
*item = Some(val);
previous
}
code:rs
pub fn replace(item: &mut Option<Item>, val: Item) -> Option<Item> {
std::mem::replace(item, Some(val)) // item の内容を Some(val) で置き換え、以前の値を返す
}
特に Option ではよく用いられるため、replace メソッドが用意されている
code:rs
pub fn replace(item: &mut Option<Item>, val: Item) -> Option<Item> {
item.replace(val) // item の中身の値を val で置き換え、以前の値を返す
}
借用ルールがあるため、複数の可変参照を引数に取る関数に、同じ値への参照を複数個渡すことはできない
code:rs
fn both_zero(left: &mut Item, right: &mut Item) {
left.contents = 0;
right.contents = 0;
}
let mut item = Item { contents: 0 };
both_zero(&mut item, &mut item);
同じ制限が、可変参照と不変参照を引数に取る関数にも適用される
code:rs
fn copy_contents(left: &mut Item, right: &Item) {
left.contents = right.contents
}
copy_contents(&mut item, &item);
Rust コンパイラは借用ルールによって、2 つの異なるポインタがメモリ上の同じアイテムを指しているかどうかを判断(Aliasing)する これにより、複数の 不変参照 が指し示すメモリの内容が、可変参照 によって変更されることが無い ことが保証されるので、以下のようなメリットを持つコードを生成できる 所有者による操作を理解するには、裏で参照を作っていると考えると分かりやすい
たとえば、アイテム所有者による変更は「短命の可変参照を作成し、それを通じて変更する」 のと等価である
したがって、他の参照がすでに存在する場合は変更できない
∵ 不変参照と可変参照が同時に存在することはできないため
code:rs
let mut item = Item { contents: 42 };
let r = &item;
item.contents = 42; // (&mut item).contents = 42 と等価
println!("reference to item is {:?}", r);
一方、不変参照を複数持つことは許されるので、読み出すことは可能
code:rs
let r = &item;
let contents = items.contents; // let contents = (&item).contents; と等価
もし r が可変参照ならば、不変参照と可変参照が同時に存在するためコンパイルエラーとなる
code:rs
let r = &mut item;
let contents = item.contents;
また、有効な参照(可変・不変問わず)がある間は、所有者は アイテム をムーブしたり、ドロップ したりすることはできない code:rs
let r = &item;
let new_item = item;
println!("reference to item is {:?}", r);
code:rs
let r = &item;
println!("reference to item is {:?}", r);
let new_item = item;
長くなったので別ページに移動 radish-miyazaki.icon