参照 r の有効期間が、指す先の value が生きている範囲にすっぽり収まっている、いちばん健全な形の例です。この形ならライフタイムを意識しなくてもそのままコンパイルが通ります。普段書いているコードのほとんどはこのパターンで、エラーになるのは次の例のような形だけです。
Rust
fn main() {
let value = 100;
let r = &value; // r は value が生きている範囲の中でだけ有効
println!("r = {}", r);
println!("value = {}", value);
} // ここで value が消え、参照 r の有効期間もここまで
2ブロックの外へ参照は持ち出せない
内側のブロックから外の値を借りるのは安全ですが、逆にブロック内で作った値への参照を外に持ち出すと「値より参照が長生き」になりコンパイルエラーになる、という対比の例です。コメントを外すと borrowed value does not live long enough というエラーを確認できます。このエラー文はライフタイム違反の典型的なサインです。
Rust
fn main() {
let outer = String::from("長生きする値");
{
let r = &outer; // 内側から外の値を借りるのは安全
println!("ブロック内: {}", r);
}
println!("ブロック後も使える: {}", outer);
// 逆向きはエラーになる(コメントを外すと確認できる)
// let r2;
// {
// let inner = String::from("すぐ消える値");
// r2 = &inner; // エラー: inner はブロックの終わりで消える
// }
// println!("{}", r2);
}
参照の有効期間はスコープの終わりまでではなく、その参照を最後に使った場所までであることを示す例です。r を最後に使った println! の後なら、同じ text を可変借用して書き換えられます。「もう使っていない参照はカウントされない」というこの賢さのおかげで、自然に書いたコードの多くがそのまま通ります。
Rust
fn main() {
let mut text = String::from("hello");
let r = &text; // 読み取り専用の借用が始まる
println!("r = {}", r); // ここが r の最後の使用 = 借用はここで終わる
text.push_str(", world"); // 借用が終わったので書き換えできる
println!("text = {}", text);
}
5設定行から借りたスライスの有効期間
設定ファイルの1行からキー部分のスライスを切り出す、実務寄りの例です。key_part が返す &str は引数 line の一部を借りているだけなので、元の line が生きている間しか使えません。引数が参照1つだけのこの形は、次のレッスンで学ぶ注釈を書かなくてもコンパイラが関係を推論してくれます。
Rust
fn key_part(line: &str) -> &str {
match line.find('=') {
Some(pos) => &line[0..pos], // 戻り値は line の一部への参照
None => line,
}
}
fn main() {
let line = String::from("timeout=30");
let key = key_part(&line); // key は line が生きている間だけ有効
println!("キー = {}", key);
println!("元の行 = {}", line);
}