このページの目次
ライフタイム
ライフタイム(
'a)は実行時の値ではなく、参照の有効スコープに対するコンパイル時の制約です。elisionルール(コンパイラによる自動推論)から明示的な注釈(struct/分散性/HRTB)に至るまで、ライフタイム注釈は本質的に借用チェッカーに対して「これらの参照のうち、誰が誰よりも長く生存するか」を伝えています。
ライフタイムはコンパイル時の概念
ライフタイムパラメータ('a)は実行時の値ではなく、コンパイル時の制約です:この参照の有効スコープは 'a より長くすることはできません。コンパイラはMIR上でリージョン推論を用いて最小のライフタイムを推論します。
ライフタイムのelision
ほとんどの関数では明示的なライフタイム注釈は不要です。コンパイラは以下の3つのルールに基づいて自動的に推論します:
- ライフタイムが注釈されていない参照パラメータには、それぞれ独立したライフタイムが割り当てられます(
fn foo<'a, 'b>(x: &'a T, y: &'b U)) - 入力ライフタイムが1つのみの場合、そのライフタイムがすべての出力ライフタイムに割り当てられます(
fn foo<'a>(x: &'a T) -> &'a U) - 複数の入力ライフタイムの中に
&selfまたは&mut selfが含まれている場合、selfのライフタイムがすべての出力に割り当てられます
ルール2が最も一般的です:単一の参照パラメータ → 返される参照はそのパラメータを借用しているとみなされます。ルール3はメソッドチェーンが動作する理由です:self の借用が返り値に渡されます。
Struct におけるライフタイム
struct が参照を含む場合、ライフタイムを明示的に注釈する必要があります:
コンパイラの保証:Excerpt<'a> のインスタンスは、参照されている &'a str より長く生存することはできません。
'static
'static は唯一「特別な」意味を持つライフタイムです。これは参照されるデータがプログラムの全期間にわたって生存することを示します。発生箇所:
- 文字列リテラル:
"hello"の型は&'static strです。コンパイル時にバイナリに埋め込まれ、常に有効です。 Box::leak(Box::new(T))により&'static mut Tを生成できます。static変数およびconst参照
分散性(Variance)
Rust のライフタイムには subtyping variance があります。理解すべき重要なルール:
'long: 'short(つまり'longは'shortより長く生存するため、'longは'shortの制約を満たす)&'a T:'aに対して共変(covariant)です('long: 'shortならば、&'long Tを&'short Tとして使用可能)&'a mut T:'aに対して不変(invariant)です(ミュータブル参照のライフタイムを短縮することはできません。そうすると aliasing XOR mutation の制約に違反する可能性があります)
let mut x: &'static str = "hello";
// これがコンパイルできると、x は y のデータを指すことになり、y が drop された後にダングリングポインタになります
HRTB(Higher-Ranked Trait Bounds)
クロージャのパラメータにライフタイムがあるが、明示的に宣言する必要がない場合:
// 「f は**任意の**ライフタイム 'a に対して Fn(&'a str) -> &'a str を満たす」
// caller 側で 'a を指定する必要はありません。コンパイラが自動的に量化します。
PhantomData
struct にライフタイムはあるが、参照を直接保持していない場合、PhantomData を使用して所有権/借用関係をマークします:
use PhantomData;
参考
- Rust Book: 第10章 3節 — ライフタイムによる参照の検証
- Rust Reference: ライフタイム elision、分散性
- RFC: RFC 2094 (NLL)、RFC 387 (Higher-Ranked Trait Bounds)
キーワード: lifetime, elision, 'static, variance, covariant, invariant, HRTB, PhantomData, region inference