Rustのライフタイムについて調べている

The Rust Programming Languageを読んでいる。GC無しでどうやって参照を安全に管理しているのかを知りたかったのだが、10章ライフタイムまで来てようやくぼんやりと分かってきた。

Rustの全ての参照には寿命が設定されている。書かなくていい場合もあるがそれは暗黙で設定されている。参照は当然何らかの実体を指しており(nullは無い)、実体の寿命より参照の寿命が長い場合はエラーになる。C++でこれをやった場合はいわゆるダングリングポインタになる。

ここまでは何となく分かっていたのだが、問題は関数の引数に渡した参照を戻り値で返すような場合だ。関数コールはいくらでも深くできるので正確にどの実体を指しているか把握しようとすると難しい。

例えば fn longest(x: str&, y: str&) -> str& のような、文字列参照を2つ受け取って長い方を返す関数を考えてみる。寿命にしても文字列長にしてもx, yどちらが長いかは呼び出し箇所によって異なる。どのように参照安全を確保すればよいのだろうか?Rustではこの問題を解決するためにライフタイムを明示させる。(実際のところ前述の書き方ではRustはコンパイルエラーになる)

fn longest<'a>(x: str&'a, y: str&'a) -> str&'a

'aがライフタイムで、この場合はx, yと同じ寿命の文字列参照を返す、という意味になる。

呼び出し側はx, yどちらの参照が返ってくるか分からないので「longest()の結果の参照は引数の短い方の寿命だと仮定してその後のコードを処理する」ことになる(ここが賢いところ)。確かによく考えたらコンパイラが実際の寿命よりも仮定すればコードは安全になる。

メンバ変数の参照にもライフタイムが設定されており、自分よりも早く実体が消えるものの参照はメンバ変数に代入できない。基本的な考え方は同じということだ。

まとめると

  • 寿命が自分よりも短い実体の参照は保持できない
  • 関数呼び出しで複数の候補から参照を返すような場合は、その中で最も短いものが返って来ると仮定して静的に解析する

となる。寿命のワーストケースを仮定しながら解決していけば静的に参照安全を保証できるのか。