Chapter 2: 複雑なライフタイム

学習目標

この章を読み終えると、以下のことができるようになります:

  • 複数のライフタイムパラメータを持つ関数を設計できる
  • ライフタイム省略規則を完全に理解し、予測できる
  • ライフタイム境界(T: 'a)を適切に使える
  • Higher-Ranked Trait Bounds(for<'a>)を実戦で活用できる
  • ジェネリック型におけるライフタイムの変性を理解する
  • Rust標準ライブラリの複雑なライフタイムパターンを読み解ける
  • プロダクションコード(Kubernetes、tokio、serde)のライフタイム設計を分析できる
  • 1. 複数のライフタイムパラメータ

    1.1 基本パターン

    単一のライフタイムパラメータでは表現できない複雑な関係を扱います。

    // パターン1: 独立した2つのライフタイム
    fn choose<'a, 'b>(x: &'a str, y: &'b str, first: bool) -> (&'a str, &'b str) {
        // 'a と 'b は独立している
        // 異なるライフタイムを持つ2つの参照を返せる
        if first {
            (x, y)
        } else {
            (x, y)  // 順序は変えられない(型が異なるため)
        }
    }
    
    fn main() {
        let s1 = String::from("hello");  // 長いライフタイム
        let result;
        {
            let s2 = String::from("world");  // 短いライフタイム
            let (r1, r2) = choose(&s1, &s2, true);
            println!("{} {}", r1, r2);
        } // s2 はここで終了
          // r2 も無効になる
        // println!("{}", result);  // エラー
    }
    

    視覚化:

    時間軸 ────────────────────────────►
    
    's1:     ┌──────────────────────────────┐
             │                              │
    's2:     │    ┌──────────┐              │
             │    │          │              │
             └────┴──────────┴──────────────┘
                  ↑          ↑
                  r1 ('a)    r2 ('b)
    
    独立したライフタイム: 'a と 'b は無関係
    

    1.2 包含関係のある複数ライフタイム

    // パターン2: 'a outlives 'b
    fn select<'a, 'b>(x: &'a str, y: &'b str, flag: bool) -> &'b str
    where
        'a: 'b,  // 'a は 'b より長く生きる
    {
        // xのライフタイムは'a、yは'b
        // 戻り値は'bなので、両方返せる('aは'bより長い)
        if flag {
            x  // OK: &'a str は &'b str として扱える(covariant)
        } else {
            y  // OK: 同じ型
        }
    }
    
    fn main() {
        let long = String::from("long-lived");  // 'a: 長い
        let short;
        {
            let temp = String::from("short-lived");  // 'b: 短い
            short = select(&long, &temp, true);
            println!("{}", short);
        } // 'b の終了
          // short はここでは使えない
    }
    

    視覚化:

    時間軸 ────────────────────────────►
    
    'a:      ┌──────────────────────────────┐
             │                              │
    'b:      │    ┌──────────┐              │
             │    │          │              │
             └────┴──────────┴──────────────┘
    
    'a: 'b が成立('a は 'b を包含)
    戻り値は 'b → 'a も 'b も返せる
    

    1.3 共通ライフタイムの計算

    // パターン3: 最小共通ライフタイム
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        // 'a は x と y の最小共通ライフタイム
        if x.len() > y.len() { x } else { y }
    }
    
    fn main() {
        let s1 = String::from("long string");
        let result;
        {
            let s2 = String::from("short");
            // 'a = 's1 ∩ 's2 = 's2(短い方)
            result = longest(&s1, &s2);
            println!("{}", result);
        } // 's2 の終了 = 'a の終了
          // println!("{}", result);  // エラー!'a は終了している
    }
    

    形式的定義:

    'a = 's1 ∩ 's2
    
    where:
      ∩: ライフタイムの積(最小共通期間)
      'a ⊆ 's1 ∧ 'a ⊆ 's2
      ∀'x. ('x ⊆ 's1 ∧ 'x ⊆ 's2) ⇒ 'x ⊆ 'a
    

    1.4 構造体での複数ライフタイム

    // 2つの異なるライフタイムを持つ構造体
    struct RefPair<'a, 'b> {
        first: &'a str,
        second: &'b str,
    }
    
    impl<'a, 'b> RefPair<'a, 'b> {
        // コンストラクタ: 2つの独立したライフタイム
        fn new(first: &'a str, second: &'b str) -> Self {
            RefPair { first, second }
        }
    
        // 'a を返すメソッド
        fn get_first(&self) -> &'a str {
            self.first
        }
    
        // 'b を返すメソッド
        fn get_second(&self) -> &'b str {
            self.second
        }
    
        // 自己参照のライフタイムも考慮
        fn get_first_ref<'s>(&'s self) -> &'s &'a str {
            // 戻り値のライフタイムは 's
            // しかし内容は 'a
            &self.first
        }
    }
    
    fn main() {
        let long = String::from("long-lived");
        let pair;
        {
            let short = String::from("short-lived");
            pair = RefPair::new(&long, &short);
            // pair 全体のライフタイムは 'short まで
            println!("{} {}", pair.first, pair.second);
        } // short が終了 → pair も使えなくなる
          // println!("{}", pair.first);  // エラー
    }
    

    ライフタイム制約:

    RefPair の有効期間:
      min('a, 'b) まで
    
    理由:
      構造体のライフタイムは、全てのフィールドの
      ライフタイムの最小値
    

    ---

    2. ライフタイム省略規則(Elision Rules)の詳細

    2.1 3つの省略規則

    Rustコンパイラは、以下の規則でライフタイムを自動推論します。

    // ────────────────────────────────────────────────────
    // 規則1: 各入力参照に異なるライフタイムを割り当てる
    // ────────────────────────────────────────────────────
    
    // 書いたコード
    fn first_word(s: &str) -> &str { /* ... */ }
    
    // コンパイラが補完
    fn first_word<'a>(s: &'a str) -> &'a str { /* ... */ }
    
    // ────────────────────────────────────────────────────
    // 規則2: 入力ライフタイムが1つなら、出力に伝播
    // ────────────────────────────────────────────────────
    
    // 書いたコード
    fn identity(x: &i32) -> &i32 { x }
    
    // コンパイラが補完
    fn identity<'a>(x: &'a i32) -> &'a i32 { x }
    
    // ────────────────────────────────────────────────────
    // 規則3: メソッドの場合、&self のライフタイムを出力に伝播
    // ────────────────────────────────────────────────────
    
    // 書いたコード
    impl<T> MyStruct<T> {
        fn get_ref(&self) -> &T { &self.value }
    }
    
    // コンパイラが補完
    impl<T> MyStruct<T> {
        fn get_ref<'a>(&'a self) -> &'a T { &self.value }
    }
    

    2.2 省略できないケース

    // ケース1: 複数の入力、1つの出力
    // → どのライフタイムを返すか不明確
    fn longest(x: &str, y: &str) -> &str {  // エラー!
        if x.len() > y.len() { x } else { y }
    }
    
    // 正しい書き方
    fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
        if x.len() > y.len() { x } else { y }
    }
    
    // ケース2: 関数ポインタ/クロージャ
    // → 省略規則が適用されない
    fn apply(f: fn(&i32) -> &i32, x: &i32) -> &i32 {  // エラー!
        f(x)
    }
    
    // 正しい書き方: HRTBs使用
    fn apply<F>(f: F, x: &i32) -> &i32
    where
        F: for<'a> Fn(&'a i32) -> &'a i32,
    {
        f(x)
    }
    
    // ケース3: トレイトメソッドでの複雑な関係
    trait Parser {
        fn parse(&self, input: &str) -> Result<&str, &str>;  // エラー!
    }
    
    // 正しい書き方
    trait Parser {
        fn parse<'a, 'b>(&'a self, input: &'b str) -> Result<&'b str, &'b str>;
    }
    

    2.3 省略規則の形式的定義

    アルゴリズム:
    
    1. 全ての入力参照にライフタイムパラメータを割り当て
       fn foo(x: &T, y: &U)
       → fn foo<'a, 'b>(x: &'a T, y: &'b U)
    
    2. 入力ライフタイムが1つの場合、全ての出力に伝播
       fn foo<'a>(x: &'a T) -> &U
       → fn foo<'a>(x: &'a T) -> &'a U
    
    3. 入力に &self または &mut self がある場合、
       そのライフタイムを全ての出力に伝播
       fn foo<'a>(&'a self, x: &T) -> &U
       → fn foo<'a, 'b>(&'a self, x: &'b T) -> &'a U
    
    4. 上記で決まらない場合、エラー
    

    2.4 実践的な例

    // 標準ライブラリの例: str::split
    impl str {
        // 書かれているコード
        pub fn split<'a, P>(&'a self, pat: P) -> Split<'a, P>
        where
            P: Pattern<'a>,
        {
            // ...
        }
    
        // 省略規則を適用すると
        pub fn split<P>(&self, pat: P) -> Split<'_, P>
        where
            P: Pattern,
        {
            // '_: 省略されたライフタイム
        }
    }
    
    // 使用例
    fn main() {
        let s = String::from("hello world");
        let words: Vec<&str> = s.split(' ').collect();
        // words の要素のライフタイムは 's と同じ
    }
    

    ---

    3. ライフタイム境界(Lifetime Bounds): T: 'a

    3.1 基本概念

    // T: 'a の意味:
    // 「型Tは少なくともライフタイム'aの間有効である」
    
    // 例1: ジェネリック構造体
    struct Ref<'a, T>
    where
        T: 'a,  // T は 'a より長く生きる
    {
        value: &'a T,
    }
    
    // なぜ必要?
    // &'a T という参照を持つためには、T 自体が 'a より長生きしないといけない
    

    視覚化:

    時間軸 ────────────────────────────►
    
    'a:      ┌──────────────┐
             │              │
    T:       ┌──────────────────────┐
             │              │       │
             └──────────────┴───────┘
                    ↑
              T は 'a を包含
              T: 'a が成立
    

    3.2 実践例

    // 例2: 参照を持つジェネリック関数
    fn store<'a, T>(value: &'a T) -> Box<dyn Fn() -> &'a T + 'a>
    where
        T: 'a,  // T は 'a より長く生きる必要がある
    {
        // クロージャが 'a の参照を返すため、
        // T も 'a より長生きしないといけない
        Box::new(move || value)
    }
    
    // 例3: 構造体のimpl
    impl<'a, T> Ref<'a, T>
    where
        T: 'a,
    {
        fn new(value: &'a T) -> Self {
            Ref { value }
        }
    
        fn get(&self) -> &'a T {
            self.value
        }
    }
    
    fn main() {
        let x = 42;
        let r = Ref::new(&x);
        println!("{}", r.get());
    }
    

    3.3 静的ライフタイム境界: T: 'static

    // 'static: プログラム全体で有効
    // T: 'static の意味: T は参照を含まない、または 'static 参照のみ
    
    // 例1: スレッドに送れる型
    use std::thread;
    
    fn spawn_thread<T>(value: T)
    where
        T: Send + 'static,  // T は 'static でないといけない
    {
        thread::spawn(move || {
            // value を使用
            println!("In thread: {:?}", value);
        });
    }
    
    // OK: i32 は参照を含まないので 'static
    spawn_thread(42);
    
    // OK: String は所有型なので 'static
    spawn_thread(String::from("hello"));
    
    // エラー: &str は非 'static 参照
    let s = String::from("local");
    // spawn_thread(&s);  // エラー!&str は 'static ではない
    
    // OK: &'static str は 'static
    spawn_thread("static string literal");
    

    3.4 複雑な境界

    // 複数のライフタイム境界
    struct Complex<'a, 'b, T, U>
    where
        T: 'a,       // T は 'a より長生き
        U: 'b,       // U は 'b より長生き
        'a: 'b,      // 'a は 'b を包含
    {
        t_ref: &'a T,
        u_ref: &'b U,
    }
    
    impl<'a, 'b, T, U> Complex<'a, 'b, T, U>
    where
        T: 'a,
        U: 'b,
        'a: 'b,
    {
        fn new(t: &'a T, u: &'b U) -> Self {
            Complex { t_ref: t, u_ref: u }
        }
    
        // 't は 'a より短い
        fn get_t<'t>(&'t self) -> &'t T
        where
            'a: 't,  // 'a outlives 't
        {
            self.t_ref
        }
    }
    

    ---

    4. Higher-Ranked Trait Bounds(HRTBs)の実践

    4.1 関数ポインタとクロージャ

    // 問題: 任意のライフタイムで動作するクロージャを受け取りたい
    
    // 間違い: ライフタイムが固定される
    fn call_with_ref<'a, F>(f: F, x: &'a i32)
    where
        F: Fn(&'a i32) -> &'a i32,  // 'a は関数シグネチャで決まる
    {
        f(x);
    }
    
    // 正しい: HRTBs使用
    fn call_with_ref_hrtb<F>(f: F, x: &i32)
    where
        F: for<'a> Fn(&'a i32) -> &'a i32,  // 任意の 'a で動作
    {
        f(x);
    }
    
    fn main() {
        let identity = |x: &i32| x;
        let value = 42;
        call_with_ref_hrtb(identity, &value);
    }
    

    4.2 トレイトオブジェクトでのHRTBs

    // 例: 任意のライフタイムのストリングを処理できるパーサー
    trait Parser {
        // 任意の入力ライフタイムで動作
        fn parse<'a>(&self, input: &'a str) -> Result<&'a str, &'a str>;
    }
    
    // パーサーを受け取る関数
    fn use_parser<P>(parser: &P, input: &str) -> Result<&str, &str>
    where
        P: Parser,
    {
        parser.parse(input)
    }
    
    // トレイトオブジェクト化
    struct IdentityParser;
    
    impl Parser for IdentityParser {
        fn parse<'a>(&self, input: &'a str) -> Result<&'a str, &'a str> {
            Ok(input)
        }
    }
    
    fn main() {
        let parser = IdentityParser;
        let input = String::from("hello");
        match use_parser(&parser, &input) {
            Ok(result) => println!("Parsed: {}", result),
            Err(e) => println!("Error: {}", e),
        }
    }
    

    4.3 標準ライブラリの例: Fn トレイト

    // Fn トレイトの実際の定義(簡略版)
    pub trait Fn<Args>: FnMut<Args> {
        extern "rust-call" fn call(&self, args: Args) -> Self::Output;
    }
    
    // 実際には、以下のように使われる
    fn apply_to_3<F>(f: F) -> i32
    where
        F: Fn(i32) -> i32,  // 実は F: for<'a> Fn(&'a i32) のような扱い
    {
        f(3)
    }
    
    // 複雑な例: 参照を取るクロージャ
    fn map_ref<F>(f: F, x: &str) -> usize
    where
        F: for<'a> Fn(&'a str) -> usize,
    {
        f(x)
    }
    
    fn main() {
        let length = map_ref(|s| s.len(), "hello");
        println!("Length: {}", length);
    }
    

    4.4 実践的なケーススタディ

    // ケース1: イテレータの変換
    struct MapRef<I, F> {
        iter: I,
        func: F,
    }
    
    impl<'a, I, F, T, U> Iterator for MapRef<I, F>
    where
        I: Iterator<Item = &'a T>,
        F: for<'b> Fn(&'b T) -> &'b U,
        T: 'a,
        U: 'a,
    {
        type Item = &'a U;
    
        fn next(&mut self) -> Option<Self::Item> {
            self.iter.next().map(|x| (self.func)(x))
        }
    }
    
    // ケース2: 非同期トレイト(概念的)
    trait AsyncFn {
        // 任意のライフタイムで Future を返す
        fn call<'a>(&'a self) -> impl Future<Output = ()> + 'a;
    }
    

    ---

    5. ジェネリック型におけるライフタイムの変性

    5.1 変性の実践的理解

    // Covariant(共変)の例: Vec<T>
    fn covariant_vec() {
        let v: Vec<&'static str> = vec!["hello"];
    
        // Vec<&'static str> は Vec<&'a str> として使える('a は 'static より短い)
        fn print_vec<'a>(v: Vec<&'a str>) {
            for s in v {
                println!("{}", s);
            }
        }
    
        print_vec(v);  // OK: Vec は T について covariant
    }
    
    // Invariant(不変)の例: Cell<T>
    use std::cell::Cell;
    
    fn invariant_cell() {
        let c: Cell<&'static str> = Cell::new("hello");
    
        // エラー: Cell<&'static str> は Cell<&'a str> として使えない
        // fn use_cell<'a>(c: Cell<&'a str>) { }
        // use_cell(c);  // エラー!Cell は T について invariant
    }
    

    5.2 PhantomData と変性の制御

    use std::marker::PhantomData;
    
    // PhantomData を使って変性を制御
    struct MyType<'a, T>
    where
        T: 'a,
    {
        // PhantomData<&'a T>: 'a と T について covariant
        _marker: PhantomData<&'a T>,
        ptr: *const T,  // 生ポインタは実際には使わない
    }
    
    // 実例: 独自のスマートポインタ
    struct MyBox<T> {
        ptr: *mut T,
        _marker: PhantomData<T>,  // T について covariant & owned
    }
    
    impl<T> MyBox<T> {
        fn new(value: T) -> Self {
            let ptr = Box::into_raw(Box::new(value));
            MyBox {
                ptr,
                _marker: PhantomData,
            }
        }
    
        fn get(&self) -> &T {
            unsafe { &*self.ptr }
        }
    }
    
    impl<T> Drop for MyBox<T> {
        fn drop(&mut self) {
            unsafe {
                let _ = Box::from_raw(self.ptr);
            }
        }
    }
    

    5.3 変性の表(拡張版)

    T の変性 'a の変性 理由
    `&'a T` Covariant Covariant 読み取り専用、安全に縮められる
    `&'a mut T` Covariant Invariant 書き込み可能、ライフタイムは固定
    `*const T` Covariant - 生ポインタ、読み取り専用
    `*mut T` Invariant - 生ポインタ、書き込み可能
    `fn(T) -> U` Contravariant (T), Covariant (U) - 引数は反変、戻り値は共変
    `Cell` Invariant - 内部可変性、サブタイピング不可
    `UnsafeCell` Invariant - 全ての内部可変性の基礎
    `PhantomData` Covariant - T を所有するかのように振る舞う
    `PhantomData<&'a T>` Covariant Covariant 不変参照のように振る舞う
    `PhantomData<&'a mut T>` Covariant Invariant 可変参照のように振る舞う

    ---

    6. Rust標準ライブラリのライフタイムパターン

    6.1 Option と Result

    // Option の定義(簡略版)
    pub enum Option<T> {
        Some(T),
        None,
    }
    
    impl<T> Option<T> {
        // as_ref: &Option<T> → Option<&T>
        pub fn as_ref(&self) -> Option<&T> {
            match self {
                Some(x) => Some(x),
                None => None,
            }
        }
    
        // map: ライフタイムの変換
        pub fn map<U, F>(self, f: F) -> Option<U>
        where
            F: FnOnce(T) -> U,
        {
            match self {
                Some(x) => Some(f(x)),
                None => None,
            }
        }
    }
    
    // 使用例
    fn main() {
        let s = Some(String::from("hello"));
        let r: Option<&str> = s.as_ref().map(|s| s.as_str());
        // s: Option<String>
        // s.as_ref(): Option<&String>
        // .map(|s| s.as_str()): Option<&str>
    }
    

    6.2 Iterator

    // Iterator の map 実装(簡略版)
    pub trait Iterator {
        type Item;
    
        fn next(&mut self) -> Option<Self::Item>;
    
        // map: ライフタイムを保持
        fn map<B, F>(self, f: F) -> Map<Self, F>
        where
            Self: Sized,
            F: FnMut(Self::Item) -> B,
        {
            Map::new(self, f)
        }
    }
    
    // filter_map: ライフタイムの複雑な例
    impl<I: Iterator> Iterator for I {
        fn filter_map<B, F>(self, f: F) -> FilterMap<Self, F>
        where
            Self: Sized,
            F: FnMut(Self::Item) -> Option<B>,
        {
            FilterMap::new(self, f)
        }
    }
    
    // 実例: 文字列のイテレータ
    fn main() {
        let s = String::from("hello world");
        let words: Vec<&str> = s.split_whitespace().collect();
        // split_whitespace() → SplitWhitespace<'_>
        // イテレータのライフタイムは s のライフタイムと同じ
    }
    

    6.3 Cow (Clone-on-Write)

    use std::borrow::Cow;
    
    // Cow の定義(簡略版)
    pub enum Cow<'a, B>
    where
        B: 'a + ToOwned + ?Sized,
    {
        Borrowed(&'a B),
        Owned(<B as ToOwned>::Owned),
    }
    
    impl<'a> Cow<'a, str> {
        // 借用から所有への変換
        pub fn into_owned(self) -> String {
            match self {
                Cow::Borrowed(s) => s.to_owned(),
                Cow::Owned(s) => s,
            }
        }
    
        // 可変参照を取得(必要に応じてクローン)
        pub fn to_mut(&mut self) -> &mut String {
            match self {
                Cow::Borrowed(s) => {
                    *self = Cow::Owned(s.to_owned());
                    match self {
                        Cow::Owned(s) => s,
                        _ => unreachable!(),
                    }
                }
                Cow::Owned(s) => s,
            }
        }
    }
    
    // 実例
    fn process_text(input: &str) -> Cow<str> {
        if input.contains("foo") {
            // 変更が必要: 所有型を返す
            Cow::Owned(input.replace("foo", "bar"))
        } else {
            // 変更不要: 借用のまま
            Cow::Borrowed(input)
        }
    }
    
    fn main() {
        let text = "hello foo world";
        let result = process_text(text);
        println!("{}", result);  // "hello bar world"
    }
    

    ---

    7. プロダクションコードのケーススタディ

    7.1 Kubernetes client-go (Rust移植版)

    // Kubernetes の API レスポンス処理
    // 実際のコードの簡略版
    
    struct ApiResponse<'a> {
        // JSON の生データへの参照
        raw: &'a str,
        // パースされたメタデータ
        metadata: Metadata<'a>,
    }
    
    struct Metadata<'a> {
        name: &'a str,
        namespace: &'a str,
        labels: HashMap<&'a str, &'a str>,
    }
    
    impl<'a> ApiResponse<'a> {
        fn parse(raw: &'a str) -> Result<Self, Error> {
            // raw を解析してメタデータを抽出
            // 全てのフィールドが raw へのスライスなので、
            // コピー不要(ゼロコピー解析)
            let metadata = Metadata {
                name: extract_name(raw)?,
                namespace: extract_namespace(raw)?,
                labels: extract_labels(raw)?,
            };
            Ok(ApiResponse { raw, metadata })
        }
    
        fn get_name(&self) -> &'a str {
            // 'a のライフタイムを保持
            self.metadata.name
        }
    }
    
    // 使用例
    fn main() {
        let json = r#"{"metadata": {"name": "pod-1", "namespace": "default"}}"#;
        let response = ApiResponse::parse(json).unwrap();
        println!("Name: {}", response.get_name());
        // json が生きている限り response も有効
    }
    

    7.2 tokio の非同期ランタイム

    // tokio の JoinHandle 簡略版
    use std::future::Future;
    use std::pin::Pin;
    
    pub struct JoinHandle<T> {
        // タスクの結果を持つ Future
        // 'static: タスクは独立して実行される
        inner: Pin<Box<dyn Future<Output = T> + Send + 'static>>,
    }
    
    // タスクをスポーン
    pub fn spawn<T>(future: T) -> JoinHandle<T::Output>
    where
        T: Future + Send + 'static,
        T::Output: Send + 'static,
    {
        // future は 'static でないといけない
        // 理由: タスクは別スレッドで実行される可能性がある
        JoinHandle {
            inner: Box::pin(future),
        }
    }
    
    // 使用例
    async fn async_task() {
        // OK: 参照を含まない
        let handle = spawn(async {
            42
        });
    
        // エラー: 参照を含む
        let x = 42;
        // let handle = spawn(async {
        //     &x  // エラー!'static ではない
        // });
    }
    

    7.3 serde の Deserialize

    // serde の Deserialize トレイト(簡略版)
    pub trait Deserialize<'de>: Sized {
        // 'de: デシリアライズ元のデータのライフタイム
        fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
        where
            D: Deserializer<'de>;
    }
    
    // ゼロコピーデシリアライズの例
    #[derive(Deserialize)]
    struct Config<'a> {
        // 'a: 入力データへの参照
        #[serde(borrow)]
        name: &'a str,
        #[serde(borrow)]
        value: &'a str,
    }
    
    // 使用例
    fn main() {
        let json = r#"{"name": "key", "value": "val"}"#;
        // Config のフィールドは json のスライスを指す
        let config: Config = serde_json::from_str(json).unwrap();
        println!("{}: {}", config.name, config.value);
        // json が生きている限り config も有効
    }
    

    ---

    まとめ

    この章で学んだこと

  • 複数のライフタイムパラメータ:
- 独立したライフタイム - 包含関係('a: 'b) - 最小共通ライフタイムの計算

  • ライフタイム省略規則:
- 3つの自動推論規則 - 省略できないケース - '_ の使い方

  • ライフタイム境界:
- T: 'a の意味 - T: 'static の実用例 - 複雑な境界の組み合わせ

  • HRTBs:
- for<'a> の実践的使用 - クロージャとトレイトオブジェクト - 標準ライブラリのパターン

  • 変性:
- Covariant、Contravariant、Invariant の実例 - PhantomData による変性制御 - 安全性との関係

  • 実世界の例:
- Kubernetes のゼロコピー解析 - tokio の非同期タスク - serde のデシリアライズ

次の章へ

次の章では、自己参照構造Pin とUnpin、そして安全でないライフタイム操作を学びます。

---

自己チェック問題

問題1: 複数ライフタイム

以下のコードが正しいか説明しなさい:

fn mix<'a, 'b>(x: &'a str, y: &'b str) -> (&'a str, &'a str)
where
    'b: 'a,
{
    (x, y)
}

問題2: 省略規則

以下の関数にライフタイム注釈を補完しなさい:

fn parse(input: &str) -> Result<&str, &str> {
    // ...
}

問題3: HRTBs

なぜ以下のコードは正しいか説明しなさい:

fn apply<F>(f: F) -> i32
where
    F: for<'a> Fn(&'a i32) -> &'a i32,
{
    f(&42)
}

問題4: 変性

なぜ Cell は invariant か説明しなさい。

問題5: 標準ライブラリ

Option::as_ref がどのようにライフタイムを変換するか説明しなさい。

---

参考文献

  • The Rust Reference - Lifetime Elision. https://doc.rust-lang.org/reference/lifetime-elision.html
  • Rustonomicon - Lifetimes. https://doc.rust-lang.org/nomicon/lifetimes.html
  • Rust RFC 0599 - Default object lifetime bounds. https://rust-lang.github.io/rfcs/0599-default-object-bound.html
  • Rust RFC 1214 - Clarify (and improve) rules for projections and well-formedness. https://rust-lang.github.io/rfcs/1214-projections-lifetimes-and-wf.html
  • tokio documentation. https://docs.rs/tokio/
  • serde documentation. https://docs.rs/serde/
  • Kubernetes client-go. https://github.com/kubernetes/client-go