# Rustライフタイム完全解説:アノテーション、省略規則、面接対策 2026 > Rustのライフタイムを徹底解説。ライフタイムアノテーション、省略規則、借用チェッカーの仕組みを実例で学び、技術面接に備える。 - Published: 2026-08-27 - Updated: 2026-08-27 - Author: Anthony Fillion-Maillet - Reading time: 5 min --- Rustのライフタイムは、参照がどれだけの期間有効であるかをコンパイラが追跡するための仕組みである。ガベージコレクションを持つ言語がランタイムでメモリ管理を行うのに対し、Rustの借用チェッカーはコンパイル時に参照の有効性を検証する。これにより、コードが実行される前にメモリに関するバグの多くを排除できる。 > **面接での模範解答** > > Rustにおけるライフタイムとは、参照が有効である期間を表すコンパイル時の構成要素である。借用チェッカーはライフタイムを使用して、参照が参照先のデータより長生きしないことを保証し、ランタイムオーバーヘッドなしでダングリングポインタを防止する。 ## ライフタイムがメモリ上で表すもの ライフタイムは値がどれだけ存在するかではなく、値への*参照*がどれだけ有効であるかを記述する。Rust内のすべての参照は、アノテーションが省略されていても、ライフタイムを持っている。 コンパイラが拒否する以下の関数を考えてみる。 ```rust // dangling_reference.rs fn create_dangling() -> &String { let s = String::from("hello"); &s // ERROR: `s` is dropped at end of function } ``` String型の`s`は関数スコープ内にのみ存在する。関数が戻ると、Stringのメモリが解放されるため、その参照を返すとダングリングポインタが生成される。借用チェッカーはこれをコンパイル時に検出する。 修正方法としては、所有権のある値を返すか、参照先のデータが関数呼び出しより長く存続することを保証する必要がある。 ```rust // valid_return.rs // Option 1: Return owned value fn create_owned() -> String { String::from("hello") } // Option 2: Reference data that outlives the function fn first_word(s: &str) -> &str { s.split_whitespace().next().unwrap_or("") } ``` `first_word`では、返される`&str`が入力`s`から借用しているため、`s`が有効である限り有効なままとなる。呼び出し側が入力のライフタイムを制御する。 ## ライフタイムアノテーションの構文と意味論 明示的なライフタイムアノテーションは`'a`、`'b`などの構文を使用する。これらはコンパイラへの参照の存続期間についての指示ではなく、複数の参照のライフタイム間の関係を記述するものである。 [Rustリファレンス](https://doc.rust-lang.org/reference/trait-bounds.html#lifetime-bounds)では、ライフタイムアノテーションを、参照が互いに相対的にどれだけ有効でなければならないかを制約するジェネリックパラメータとして定義している。 ```rust // lifetime_annotations.rs // Both inputs and output share the same lifetime 'a fn longest<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } fn main() { let string1 = String::from("long string"); let result; { let string2 = String::from("short"); result = longest(&string1, &string2); println!("Longest: {}", result); // Valid: both strings alive } // println!("{}", result); // ERROR: string2 dropped } ``` アノテーション`'a`はコンパイラに次のことを伝える:返される参照は`x`と`y`のライフタイムの*交差部分*の間有効である。`string2`の方がライフタイムが短いため、`string2`がドロップされた後に`result`を使用することはできない。 複数の異なるライフタイムはより複雑な関係を表現する。 ```rust // multiple_lifetimes.rs // Output tied only to first parameter's lifetime fn first_only<'a, 'b>(x: &'a str, _y: &'b str) -> &'a str { x } fn main() { let owned = String::from("owned"); let result; { let temporary = String::from("temporary"); result = first_only(&owned, &temporary); } // result still valid: only depends on owned println!("{}", result); } ``` ## Rust 2024におけるライフタイム省略規則 コンパイラはアノテーションが省略された場合、3つの省略規則を適用してライフタイムを推論する。[Rustonomicon](https://doc.rust-lang.org/nomicon/lifetime-elision.html)に文書化されているこれらの規則は、安全性を犠牲にすることなくボイラープレートを削減する。 > **3つの省略規則** > > 1. 各入力参照は独自のライフタイムパラメータを取得する > 2. 入力ライフタイムが1つだけ存在する場合、それがすべての出力参照に適用される > 3. `&self`または`&mut self`が存在する場合、そのライフタイムがすべての出力参照に適用される これらの規則は最も一般的なパターンを処理する。 ```rust // elision_examples.rs // Rule 1: Each input gets own lifetime fn takes_two(x: &str, y: &str) {} // Compiler reads: fn takes_two<'a, 'b>(x: &'a str, y: &'b str) {} // Rule 2: Single input lifetime propagates to output fn first_char(s: &str) -> &str { &s[0..1] } // Compiler reads: fn first_char<'a>(s: &'a str) -> &'a str // Rule 3: &self lifetime propagates to output impl Parser { fn peek(&self) -> &Token { &self.tokens[self.position] } // Compiler reads: fn peek<'a>(&'a self) -> &'a Token } ``` 省略が失敗すると、コンパイラは明示的なアノテーションを要求する。これは、複数の入力から派生した参照を返す関数で最も頻繁に発生する。 ```rust // elision_fails.rs // ERROR: Can't determine output lifetime fn ambiguous(x: &str, y: &str) -> &str { if x.len() > y.len() { x } else { y } } // FIX: Explicit annotation resolves ambiguity fn unambiguous<'a>(x: &'a str, y: &'a str) -> &'a str { if x.len() > y.len() { x } else { y } } ``` ## 構造体のライフタイムとoutlives関係 参照を含む構造体は、借用されたデータが構造体より長く存続しなければならないことを表現するためにライフタイムアノテーションが必要である。 ```rust // struct_lifetimes.rs struct Excerpt<'a> { text: &'a str, } impl<'a> Excerpt<'a> { // new borrows from input, so Excerpt can't outlive the source fn new(source: &'a str, start: usize, end: usize) -> Self { Excerpt { text: &source[start..end] } } // level() returns owned data, no lifetime in signature fn level(&self) -> u32 { self.text.len() as u32 / 10 } } fn main() { let novel = String::from("Call me Ishmael. Some years ago..."); let excerpt = Excerpt::new(&novel, 0, 16); println!("Excerpt: {}", excerpt.text); } // novel dropped, but excerpt already out of scope ``` `Excerpt<'a>`の`'a`は、構造体の有効性を借用された`text`のライフタイムに結び付ける。ソースStringがドロップされた後に`Excerpt`を使用しようとすると、コンパイルエラーが発生する。 複数の参照を持つ構造体では、それぞれが異なるライフタイムを持つことがある。 ```rust // multiple_struct_lifetimes.rs struct Comparison<'a, 'b> { baseline: &'a str, candidate: &'b str, } impl<'a, 'b> Comparison<'a, 'b> { fn new(baseline: &'a str, candidate: &'b str) -> Self { Comparison { baseline, candidate } } fn baseline_only(&self) -> &'a str { self.baseline } } ``` ## 'staticライフタイムとその使用場面 `'static`ライフタイムは、プログラム実行全体にわたって存続するデータを示す。文字列リテラルはバイナリに埋め込まれているため、このライフタイムを持つ。 ```rust // static_lifetime.rs let s: &'static str = "I live forever"; // Constants are implicitly 'static const CONFIG_VERSION: &str = "2.0.0"; // Thread-safe globals require 'static static COUNTER: AtomicU64 = AtomicU64::new(0); ``` 一般的なパターンとして`T: 'static`境界があるが、開発者を混乱させることが多い。この境界は`T`が`'static`でない参照を含まないことを意味し、`T`が参照でなければならないことを意味しない。 ```rust // static_bound.rs use std::thread; fn spawn_task(data: T) { thread::spawn(move || { // data moved into thread, must live independently println!("Processing in thread"); }); } fn main() { let owned = String::from("owned data"); spawn_task(owned); // OK: String is 'static (owns its data) let reference = "borrowed"; // spawn_task(reference); // OK: &'static str } ``` スレッドはスポーン元の関数より長く存続する可能性があるため、スレッドに移動されるデータはスタックローカル変数への参照を含んではならない。 ## 一般的なライフタイムエラーとその解決策 いくつかのパターンが開発者を常に悩ませる。これらを理解することでデバッグが加速する。 **ローカル変数への参照を返す:** ```rust // error_local_ref.rs fn bad_split(text: &str, delimiter: char) -> (&str, &str) { let parts: Vec<&str> = text.split(delimiter).collect(); (parts[0], parts[1]) // OK: parts[i] borrow from text, not from Vec } fn truly_bad() -> &str { let local = String::from("local"); &local // ERROR: local dropped at end of function } ``` **ライフタイムが一致しない構造体に参照を格納する:** ```rust // error_struct_lifetime.rs struct Cache { data: String, // view: &str, // ERROR: needs lifetime parameter } // Self-referential structs require unsafe or crates like ouroboros struct CacheWithView<'a> { data: String, view: Option<&'a str>, // Can't point to self.data safely } ``` フィールドが別のフィールドを参照する自己参照構造体は、unsafeコードを伴う`Pin`または[ouroboros](https://docs.rs/ouroboros/)のようなクレートが必要である。[Rustスマートポインタ](/technologies/rust/interview-questions/smart-pointers)モジュールでは、これらの高度なパターンを扱っている。 **トレイト実装でのライフタイム境界:** ```rust // trait_lifetime_bounds.rs trait Processor { fn process<'a>(&self, input: &'a str) -> &'a str; } struct Prefixer { prefix: String, } impl Processor for Prefixer { // Can't return &format!(...) - would be temporary fn process<'a>(&self, input: &'a str) -> &'a str { input // Must return input or part of it } } ``` ## ジェネリックライフタイムのための高階トレイト境界(HRTB) 高階トレイト境界は、型が特定のライフタイムだけでなく*任意の*ライフタイムでトレイトを満たす必要があることを表現するために`for<'a>`構文を使用する。 ```rust // hrtb.rs use std::fmt::Debug; // F must be callable with any lifetime 'a fn apply_to_refs(f: F) where F: for<'a> Fn(&'a str) -> &'a str, { let owned = String::from("test"); let result = f(&owned); println!("{}", result); } fn identity(s: &str) -> &str { s } fn main() { apply_to_refs(identity); } ``` HRTBはクロージャを受け入れるAPIや、様々なライフタイムの参照で動作する必要がある[Rust async/await](/blog/rust/rust-async-await-tokio-futures-concurrency)エコシステムで頻繁に登場する。 ## Rustライフタイムに関する面接質問 技術面接では、複数の深さでライフタイムの理解を調査する。これらの質問はRustポジションで定期的に出題される。 **質問:このコードがコンパイルに失敗する理由は?** ```rust // interview_q1.rs fn get_str() -> &str { "hello" } ``` **回答:** 戻り値の型には明示的なライフタイムアノテーションが必要である。文字列リテラルは`'static`ライフタイムを持つが、関数シグネチャはこれを表現していない。修正:`fn get_str() -> &'static str`。 **質問:このコードがコンパイルされる理由と、安全かどうかを説明せよ:** ```rust // interview_q2.rs fn longest<'a>(x: &'a str, _y: &str) -> &'a str { x } ``` **回答:** 出力ライフタイムが`x`にのみ結び付けられているため、このコードはコンパイルされる。戻り値が`_y`に依存しないため、`_y`パラメータは任意のライフタイムを持つことができる。これは安全である:返される参照の有効性は`x`のライフタイムにのみ依存する。 **質問:所有データと一緒に参照を格納しようとするとどうなるか?** ```rust // interview_q3.rs struct Config<'a> { name: String, description: &'a str, } ``` **回答:** このパターンは有効だが、`Config`の使用方法を制約する。構造体は`description`が参照するものより長く存続できない。自身を参照する必要がある所有データについては、両方のフィールドに`String`を使用するか、[Rustの所有権と借用](/blog/rust/rust-ownership-borrowing-demystified)で扱っている技術を検討する。 **質問:ライフタイムはトレイトオブジェクトとどのように相互作用するか?** ```rust // interview_q4.rs trait Formatter { fn format(&self, input: &str) -> String; } fn get_formatter<'a>() -> Box { // ... } ``` **回答:** トレイトオブジェクトはデフォルトで暗黙の`'static`ライフタイム境界を持つ。`Box`と明示的に書くことで、トレイトオブジェクトがライフタイム`'a`の参照を含むことを許可する。明示的な境界がない場合、`Box`は`Box`と等しい。 ## ライフタイムの変性:共変性と反変性 変性は、型がネストされているときにライフタイムがどのように関係するかを決定する。Rustの参照は以下の規則に従う。 - `&'a T`は`'a`に対して**共変**:より長いライフタイムがより短いものを代替できる - `&'a mut T`は`T`に対して**不変**:型は正確に一致する必要がある - `fn(&'a T)`は`'a`に対して**反変**:より短いライフタイムがより長いものを代替できる ```rust // variance.rs fn covariant_example() { let s: &'static str = "static"; let r: &str = s; // OK: 'static lives longer than any 'a } fn invariant_example() { let mut vec: Vec<&'static str> = vec!["a"]; // let s = String::from("local"); // vec.push(&s); // ERROR: &s is not &'static str } ``` 変性の理解は、参照を受け入れたり返したりするジェネリックAPIを設計する際に重要である。[Rustのトレイトとジェネリクス](/blog/rust/rust-traits-generics-advanced-guide)の記事では、高度なジェネリックパターンを扱っている。 ## 実践におけるライフタイムアノテーション:重要なポイント - ライフタイムは値の存在ではなく、参照の有効性を記述する。借用チェッカーはこれらを使用してコンパイル時にダングリングポインタを防止する。 - 省略規則はほとんどのケースを自動的に処理する。明示的なアノテーションは、複数の入力から派生した参照を返す際に必要になる。 - 構造体のライフタイムはoutlives関係を表現する:参照を含む構造体は、それらの参照のライフタイムでパラメータ化されなければならない。 - ジェネリクスに対する`'static`境界は「'staticでない参照を含まない」ことを意味し、「参照でなければならない」ことを意味しない。 - 自己参照構造体は、`Pin`、unsafeコード、またはヘルパークレートによる特別な処理が必要である。 - 面接質問は、コードがコンパイルに失敗する理由と、ライフタイムアノテーションが有効性の保証をどのように変えるかの理解に焦点を当てる。 --- Source: SharpSkill (https://sharpskill.dev), tech interview preparation for your real stack. HTML version of this page: https://sharpskill.dev/ja/blog/rust/rust-lifetimes-explained-annotations-elision-interview