導入
ライフタイム(lifetime) とは、「その参照が有効であり続ける期間」のことです。Rust のコンパイラ(借用チェッカー)は、すべての参照について「指す先のデータが生きている間だけ使われているか」を、このライフタイムで追跡しています。ふだんは自動なので意識しませんが、正体を知るとエラーが怖くなくなります。
説明
まず、コンパイラがライフタイムで何を見張っているのかを、具体例で。
fn main() {
let r; // r を宣言(まだ何も指さない)
{
let x = 5;
r = &x; // r は x を借りる
} // ← x がここで死ぬ(スコープを抜ける)
// println!("{r}"); // ← エラー! r は死んだ x を指している(ダングリング)
}
xのライフタイムは内側の{ }の中だけ。rはxを借りているのに、xより長く生きようとしています。- コンパイラは「借りた側(
r)が、貸した側(x)より長生きしている」ことを検出し、x does not live long enough(xの生存期間が足りない)とエラーにします。
graph TD
x["x のライフタイム<br/>(内側ブロックだけ)"]
r["r のライフタイム<br/>(外側まで続く)"]
r -->|借りている| x
note["r が x より長生き → 借用チェッカーが却下"]
r --- note
つまりライフタイムは、第5章のダングリング参照防止をあらゆる参照に一般化した仕組みです。すべての参照には「指す先のデータが生きている期間を超えて使ってはいけない」という制約があり、コンパイラがそれを機械的にチェックしています。
多くの場合、コンパイラは各参照のライフタイムを自動で推論してくれます(これを「ライフタイム省略」と呼びます)。だから普段は書かなくて済みます。ライフタイムを明示的に書く必要が出てくるのは、次のレッスンのように「コンパイラだけでは関係を判断できない」場面だけです。
試すには
上のコードの println!("{r}") のコメントを外すと、コンパイラが x の死ぬ位置を示してエラーにします。ライフタイムは「新しく覚える難しい機能」ではなく、「今まで見てきた借用の安全性を、コンパイラが期間として追跡しているだけ」だと捉えると気が楽です。