Chapter 4: 高度なパターン

学習目標

  • ライフタイム省略の高度なテクニックを習得する
  • トレイトオブジェクトとライフタイムの関係を理解する
  • 高階ライフタイムの使い方をマスターする
  • プロダクションコードでのベストプラクティスを学ぶ
  • コンパイラとの対話方法を理解する

---

4.1 高度な省略テクニック

4.1.1 暗黙的な 'static 境界

// この2つは異なる
fn foo<T>(x: T) {}
//        ^
//        T: 'static が暗黙的

fn bar<T: ?Sized>(x: &T) {}
//     ^^^^^^^^^
//     'static を要求しない

// 明示的に書くと
fn foo_explicit<T: 'static>(x: T) {}
fn bar_explicit<T: ?Sized + 'static>(x: &T) {}

理由

T を所有するには、T が 'static である必要がある
(参照を含む場合、その参照も 'static)

ただし &T の場合は T: 'static は不要
(借用しているだけなので)

4.1.2 impl Trait とライフタイム

// 戻り値の impl Trait
fn returns_iter<'a>(s: &'a str) -> impl Iterator<Item = &'a str> + 'a {
    //                                                              ^^^^
    //                                                              境界が必要
    s.split_whitespace()
}

// 省略形(Rust 2018+)
fn returns_iter_short(s: &str) -> impl Iterator<Item = &str> + '_ {
    //                                                          ^^
    //                                                          省略記法
    s.split_whitespace()
}

---

4.2 トレイトオブジェクトとライフタイム

4.2.1 dyn Trait のライフタイム

trait Trait {}

// これらは異なる
fn foo1(x: Box<dyn Trait>) {}
//            ^^^^^^^^^^^
//            暗黙的に Box<dyn Trait + 'static>

fn foo2(x: &dyn Trait) {}
//         ^^^^^^^^^^
//         暗黙的に &(dyn Trait + '_)
//         入力のライフタイムから推論

// 明示的に書くと
fn foo1_explicit(x: Box<dyn Trait + 'static>) {}
fn foo2_explicit<'a>(x: &'a (dyn Trait + 'a)) {}

4.2.2 トレイトオブジェクトの境界

trait Trait {}

struct Struct<'a> {
    trait_object: Box<dyn Trait + 'a>,
    //                          ^^^^
    //                          明示的な境界
}

impl<'a> Struct<'a> {
    fn new(obj: Box<dyn Trait + 'a>) -> Self {
        Struct { trait_object: obj }
    }
}

// 使用例
fn example<'a>(s: &'a str) -> Struct<'a> {
    struct MyType<'a>(&'a str);
    impl<'a> Trait for MyType<'a> {}

    Struct::new(Box::new(MyType(s)))
}

---

4.3 Higher-Rank Trait Bounds の高度な使用

4.3.1 複雑な HRTB

// 関数を受け取る関数
fn apply_to_all<F>(f: F)
where
    F: for<'a> Fn(&'a str) -> &'a str,
{
    let s1 = String::from("hello");
    let s2 = String::from("world");

    let r1 = f(&s1);
    let r2 = f(&s2);

    println!("{} {}", r1, r2);
}

// 使用例
fn main() {
    apply_to_all(|s| {
        if s.len() > 5 {
            &s[..5]
        } else {
            s
        }
    });
}

4.3.2 HRTB とトレイト

trait Processor {
    fn process<'a>(&self, input: &'a str) -> &'a str;
}

struct Upper;

impl Processor for Upper {
    fn process<'a>(&self, input: &'a str) -> &'a str {
        // 実際の実装は省略
        input
    }
}

// HRTB を使った境界
fn use_processor<P>(processor: P)
where
    P: for<'a> Fn(&'a str) -> &'a str,
{
    let result = processor("test");
    println!("{}", result);
}

---

4.4 ライフタイムとクロージャ

4.4.1 クロージャのキャプチャライフタイム

fn closure_lifetime<'a>(s: &'a str) -> impl Fn() -> &'a str + 'a {
    //                                                         ^^
    //                                                         クロージャの境界
    move || s
}

// より複雑な例
fn complex_closure<'a, 'b>(
    s1: &'a str,
    s2: &'b str,
) -> impl Fn() -> (&'a str, &'b str)
where
    'a: 'b,
{
    move || (s1, s2)
}

4.4.2 クロージャとHRTB

// クロージャをHRTBで制約
fn accepts_closure<F>(f: F)
where
    F: for<'a, 'b> Fn(&'a str, &'b str) -> bool,
{
    let s1 = String::from("hello");
    let s2 = String::from("world");

    println!("{}", f(&s1, &s2));
}

fn main() {
    accepts_closure(|a, b| a.len() > b.len());
}

---

4.5 実践的なデザインパターン

4.5.1 ビルダーパターンとライフタイム

struct Query<'a> {
    table: &'a str,
    conditions: Vec<(&'a str, &'a str)>,
}

struct QueryBuilder<'a> {
    table: Option<&'a str>,
    conditions: Vec<(&'a str, &'a str)>,
}

impl<'a> QueryBuilder<'a> {
    fn new() -> Self {
        QueryBuilder {
            table: None,
            conditions: Vec::new(),
        }
    }

    fn table(mut self, table: &'a str) -> Self {
        self.table = Some(table);
        self
    }

    fn where_clause(mut self, key: &'a str, value: &'a str) -> Self {
        self.conditions.push((key, value));
        self
    }

    fn build(self) -> Result<Query<'a>, &'static str> {
        Ok(Query {
            table: self.table.ok_or("table is required")?,
            conditions: self.conditions,
        })
    }
}

4.5.2 イテレータパターン

struct Parser<'a> {
    input: &'a str,
    position: usize,
}

impl<'a> Parser<'a> {
    fn new(input: &'a str) -> Self {
        Parser { input, position: 0 }
    }
}

impl<'a> Iterator for Parser<'a> {
    type Item = &'a str;

    fn next(&mut self) -> Option<Self::Item> {
        if self.position >= self.input.len() {
            return None;
        }

        let start = self.position;
        while self.position < self.input.len()
            && !self.input[self.position..].starts_with(' ')
        {
            self.position += 1;
        }

        let end = self.position;
        self.position += 1; // スペースをスキップ

        Some(&self.input[start..end])
    }
}

---

4.6 コンパイラとの対話

4.6.1 エラーメッセージの読み方

fn example() {
    let s1 = String::from("hello");
    let r;

    {
        let s2 = String::from("world");
        r = longest(&s1, &s2);
    }

    println!("{}", r);
}

fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}

エラーメッセージ

error[E0597]: `s2` does not live long enough
  --> src/main.rs:7:25
   |
7  |         r = longest(&s1, &s2);
   |                          ^^^ borrowed value does not live long enough
8  |     }
   |     - `s2` dropped here while still borrowed
9  |
10 |     println!("{}", r);
   |                    - borrow later used here

解決策

// ライフタイムを分ける
fn longest<'a, 'b>(x: &'a str, y: &'b str) -> &'a str
where
    'a: 'b,
{
    x  // yを返さない
}

4.6.2 rustc --explain

$ rustc --explain E0597

# ライフタイムエラーの詳細な説明が表示される

---

4.7 パフォーマンス最適化

4.7.1 ゼロコピーパース

// コピーを避けるデザイン
struct JsonValue<'a> {
    raw: &'a str,
}

impl<'a> JsonValue<'a> {
    fn parse(input: &'a str) -> Result<Self, Error> {
        // input をコピーせずに参照を保持
        Ok(JsonValue { raw: input })
    }

    fn as_str(&self) -> &'a str {
        self.raw
    }
}

// 使用例
fn parse_config(json: &str) -> Result<Config, Error> {
    let value = JsonValue::parse(json)?;
    // value は json のライフタイムに依存
    Ok(Config::from_json(value))
}

4.7.2 Cow の活用

use std::borrow::Cow;

fn process<'a>(input: &'a str) -> Cow<'a, str> {
    if input.contains("bad") {
        // 変更が必要な場合のみ所有
        Cow::Owned(input.replace("bad", "good"))
    } else {
        // 変更不要なら借用
        Cow::Borrowed(input)
    }
}

fn main() {
    let s1 = "hello world";
    let s2 = "bad example";

    let r1 = process(s1);  // Cow::Borrowed
    let r2 = process(s2);  // Cow::Owned

    println!("{} {}", r1, r2);
}

---

4.8 ライフタイムのテスト

4.8.1 コンパイル失敗テスト

#[cfg(test)]
mod tests {
    use super::*;

    // このテストはコンパイルエラーになるべき
    // compile_fail 属性でテスト
    #[cfg(doctest)]
    /// 
compile_fail /// fn invalid_lifetime() { /// let r; /// { /// let x = 5; /// r = &x; /// } /// println!("{}", r); /// } ///
    fn test_lifetime_error() {}
}

4.8.2 実行時テスト

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_lifetime_correctness() {
        let s1 = String::from("long string");
        let s2 = String::from("short");

        let result = longest(&s1, &s2);

        assert_eq!(result, "long string");
        // s1, s2 がまだ有効
    }
}

---

4.9 プロダクションコードのベストプラクティス

4.9.1 ドキュメント

/// パーサーを作成します。
///
/// # Lifetimes
///
/// * `'a` - 入力文字列のライフタイム
/// * `'b` - 設定オブジェクトのライフタイム
///
/// パーサーは入力と設定の両方より長く生きることはできません。
///
/// # Examples
///
/// 
/// let input = "example input"; /// let config = Config::default(); /// let parser = Parser::new(input, &config); ///
pub struct Parser<'a, 'b> {
    input: &'a str,
    config: &'b Config,
}

4.9.2 エラーハンドリング

use thiserror::Error;

#[derive(Error, Debug)]
pub enum ParseError {
    #[error("無効な入力: {0}")]
    InvalidInput(String),

    #[error("ライフタイムエラー")]
    LifetimeError,
}

pub fn parse<'a>(input: &'a str) -> Result<Value<'a>, ParseError> {
    if input.is_empty() {
        return Err(ParseError::InvalidInput("空の入力".to_string()));
    }

    Ok(Value { data: input })
}

---

4.10 まとめ

4.10.1 ライフタイムマスタリーのチェックリスト

✓ ライフタイムの形式的理解
✓ 省略規則の完全な理解
✓ HRTB の使いこなし
✓ Pin と自己参照の理解
✓ トレイトオブジェクトとライフタイム
✓ パフォーマンス最適化
✓ エラーメッセージの解読
✓ プロダクションコードへの応用

4.10.2 次のステップ

  • 実践
- 実際のプロジェクトで応用 - オープンソースに貢献

  • 深掘り
- Rustコンパイラのソースを読む - Polonius(次世代借用チェッカー)を学ぶ

  • コミュニティ
- Rust Users Forum で質問・回答 - ブログ記事を書く

---

練習問題

問題1

複雑なライフタイム関係を持つAPIを設計し、そのライフタイム制約を説明しなさい。

問題2

HRTBを使った汎用的な関数を実装し、その利点を説明しなさい。

問題3

ゼロコピーパーサーを実装し、パフォーマンスを測定しなさい。

---

コース完了

Rust Lifetime Mastery コース完了おめでとうございます!

あなたは now Rust のライフタイムシステムを深く理解しました:

  • 理論的基盤
  • 実践的なパターン
  • 高度なテクニック
  • プロダクションでのベストプラクティス

次のステップ:

  • 実際のプロジェクトでこの知識を活用
  • より深いコンパイラの理解
  • コミュニティへの貢献

Happy Coding with Rust! 🦀