Rustでは、厳格なメモリ所有システムのため、コンパイラが参照の正当性を検証するためのライフタイム(lifetimes)メカニズムが導入されました。しかし、手動でライフタイム注釈を指定するのは煩雑ですので、コンパイラが自動的にライフタイムを推論できるルール「エリジョン」が言語に導入されました。
参照のライフタイムを正しく管理しないと、ダングリングポインタ(dangling pointers)やメモリ競合のエラーが発生する可能性があります。プログラマが常に明示的にライフタイムを指定する必要がある場合、開発が非常に複雑になります。
Rustコンパイラはライフタイムエリジョンのルールを使用して、一般的な関数シグネチャで、入力された参照と返される値の間のライフタイムが自動的にどのように結びつくべきかを決定します。これにより、テンプレートコードの量が減り、APIがより明快になります。そのため、安全性も保たれます。
コードの例:
fn get_first(s: &str) -> &str { // ライフタイムエリジョン &s[..1] }
ここでコンパイラは、結果のライフタイムを推論します - これは入力パラメータsのライフタイムと等しくなります。
主な特徴:
なぜライフタイムを常に省略し、エリジョンのルールに頼ることはできませんか?
エリジョンは「単純な」状況でのみ機能します。たとえば、関数が入力された参照の1つを返す場合、コンパイラはそのライフタイムを結びつけることができますが、複数の曖昧な関係がある場合、コンパイルエラーが発生し、すべてを明示的に注釈を付ける必要があります。
fn pick<'a>(a: &'a str, b: &'a str, first: bool) -> &'a str { if first { a } else { b } } // ここでは'aを明示的に指定する必要があります。さもないとコンパイラは関係を理解できません。
構造体が参照フィールドのみを含む場合、ライフタイムを省略できますか?
いいえ、構造体が参照フィールドを含む場合、インスタンスがそのデータを超えて生存しないことを保証するためにライフタイムパラメータを持たなければなりません。
struct Foo<'a> { data: &'a str, }
ローカル変数への参照を返そうとした場合、どうなりますか?
コンパイラはエラーを出します。形式的にはエリジョンのルールがライフタイムを「推論」できる可能性がある場合でも、Rustはライフタイムをタイプだけでなく、スコープによっても追跡します。
プログラマは、関数内のローカル一時バッファへの参照を返すライフタイムを明示的に記述しないAPIを書きました。コンパイラはコードを拒否しましたが、エラーを「回避」しようとした際に不正なライフタイム注釈が追加され、その結果、いくつかの混乱したエラーが発生しました。
利点:
欠点:
ライブラリのAPIでは、実際に必要な場合にのみ正しいライフタイム注釈が使用されます。それ以外は、エリジョンの自動ルールによってカバーされており、コードは簡潔で理解しやすくなっています。
利点:
欠点: