4 分で読了 #rust #error-handling
このページの目次

Result と Option

Option は「値が存在しない可能性がある」ことを表し、Result は「操作が失敗する可能性がある」ことを表します。これらは例外ではなく列挙型です。? 演算子は None/Err に遭遇すると早期リターンし、combinator チェーン (map/and_then/or_else) により、エラー処理は try-catch による制御フローのジャンプではなく、型安全なパイプラインとなります。

Option: 値が存在しない可能性がある

fn find_user(id: u64) -> Option<User> { ... }

find_user(42)
    .map(|u| u.name)                 // Option<User> → Option<String>、変換
    .and_then(|name| db_lookup(&name)) // Option<String> → Option<Record>、チェーン
    .filter(|r| r.active)            // active のみ保持
    .unwrap_or(default_record);      // 抽出またはデフォルト値を使用

各 combinator は Some の場合のみ処理し、None は自動的にショートサーキットします。このパターンは関数型プログラミングにおいて「レールウェイ指向プログラミング」と呼ばれます。成功の軌道に沿って値を渡すか、失敗の軌道に一瞬でジャンプしてそのままそこに留まり、null チェックは行われません。unwrap_or の時点でコンパイラがデフォルト値の提供を強制するため、値の欠落に対する処理を見逃すことはありません。

Result: エラーは値であり、例外ではない

fn parse(s: &str) -> Result<i32, ParseIntError> { s.parse() }

let n = match parse("42") {
    Ok(n) => n * 2,
    Err(e) => { eprintln!("parse failed: {}", e); return; }
};

例外との核心的な違い:⁠エラーは戻り値の一部であり、呼び出し元は関数のシグネチャからそれが失敗する可能性があることを知ることができます。隠れた制御フローはなく、「この関数はどのような例外を投げるか」といったことを覚える必要もありません。コンパイラは Result の処理を強制し、#[must_use] 属性により未使用の Result に対してコンパイル警告が発生します。

? 演算子: エラーの早期リターン

fn read_config(path: &str) -> Result<Config, Error> {
    let file = std::fs::read_to_string(path)?;    // Err → 早期リターン
    let config: Config = toml::from_str(&file)?;
    Ok(config)
}

展開した同等のコードは以下の通りです:

let file = match std::fs::read_to_string(path) {
    Ok(v) => v,
    Err(e) => return Err(e.into()),  // .into() は From トレイトを通じてエラー型を変換
};

?From::from を呼び出して自動的なエラー型の変換を行います。From<io::Error> for MyError が実装されていれば、std::fs::read_to_string()?io::Error は自動的に MyError に変換されます。これが Rust におけるエラー伝播の鍵であり、各レイヤーで手動でエラーをラップする必要がありません。

main で Result を返す

fn main() -> Result<(), Box<dyn Error>> {
    let config = read_config()?;     // main 内での ?!
    Ok(())
}

mainResult を返す場合、mainErr を返すと、ランタイムがエラーを出力して終了します(終了コード 1)。これは単純な CLI ツールに適しています。複雑なアプリケーションでは、通常、独自のエラー処理とログ記録が行われます。

参考

  • Rust Book: 第 9 章
  • Rust By Example: Option/Result, ?

Keywords: Option, Result, combinator, and_then, ?, error propagation, From trait, #[must_use]