導入
たいていライフタイムは自動推論されますが、関数が参照を受け取って参照を返すとき、コンパイラが「返す参照は、どの引数と同じ寿命か」を判断できないことがあります。そのときだけ、ライフタイム注釈でヒントを与えます。Rust 特有の <'a> という記法の意味を解きほぐしましょう。
説明
2つの文字列スライスのうち、長い方を返す関数を書いてみます。
fn longest(x: &str, y: &str) -> &str { // ← これは実はコンパイルエラーになる
if x.len() > y.len() { x } else { y }
}
これはエラーになります。コンパイラの言い分はこうです——「返ってくる参照は x かもしれないし y かもしれない。呼び出し側では、その参照がいつまで有効だと思えばいい? x の寿命? y の寿命? 判断できない」。
そこで、ライフタイム注釈で「これらの参照は同じ寿命 'a を共有する」と教えます。
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
if x.len() > y.len() { x } else { y }
}
<'a>…'a(アポストロフィ + 名前)というライフタイム引数を宣言。「ある寿命を'aと呼ぶことにする」という意味で、具体的な期間を決めるものではありません。x: &'a str, y: &'a str… 「xとyは、少なくとも'aの期間は有効」。-> &'a str… 「戻り値の参照も'aの期間だけ有効」。
これで意味が定まります。「戻り値は、x と y の寿命のうち短い方の期間だけ有効」。コンパイラはこの約束をもとに、呼び出し側でも安全性をチェックできます。たとえば「x か y のどちらかが先に死んだあとに戻り値を使う」コードは、この注釈のおかげでコンパイル時に弾かれます。
大事なのは、ライフタイム注釈は寿命を「変える」のではなく、参照どうしの関係を「説明する」だけという点です。ラベルを貼って「これとこれは連動している」と伝えるだけで、実際の生存期間は変わりません。
なお、多くの関数では注釈は不要です。たとえば第6章の first_word(text: &str) -> &str は、戻り値が明らかに唯一の引数 text に紐づくので、コンパイラが自動で 'a を補います(省略ルール)。注釈を書くのは「入力が複数あって、出力がどれに紐づくか曖昧」なときだけ、と覚えておけば十分です。
試すには
注釈なしの longest はエラー、<'a> を付けると通ります。エラーメッセージの missing lifetime specifier(ライフタイム指定子が足りない)は「返す参照がどの入力に紐づくか教えて」という意味だと分かれば、対処は機械的です。最初は「言われたら足す」で構いません。