Progrust Library.

// article

Rust エラーハンドリングのパターン集.

Rust エラーハンドリングのパターン集

Rust のエラー処理は明示的で安全です。本記事では、実践的なパターンを紹介します。

パターン 1: ? 演算子による早期リターン

fn read_and_parse(path: &str) -> Result<i32, Box<dyn std::error::Error>> {
    let content = std::fs::read_to_string(path)?;
    let number: i32 = content.trim().parse()?;
    Ok(number)
}

パターン 2: map による変換

fn get_user_name(id: u32) -> Result<String, String> {
    find_user(id)
        .map(|user| user.name)
        .map_err(|_| "User not found".to_string())
}
map と map_err
  • map: Ok の値を変換
  • map_err: Err の値を変換(通常はエラー型の変換に使う)
  • and_then: 戻り値が Result な関数と組み合わせる

パターン 3: unwrap_or_else

fn get_config() -> Config {
    load_config()
        .unwrap_or_else(|_| Config::default())
}

パターン 4: カスタムエラー型

use std::fmt;

#[derive(Debug)]
enum ParseError {
    InvalidFormat,
    OutOfRange,
}

impl fmt::Display for ParseError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            ParseError::InvalidFormat => write!(f, "Invalid format"),
            ParseError::OutOfRange => write!(f, "Out of range"),
        }
    }
}

impl std::error::Error for ParseError {}

fn parse_age(input: &str) -> Result<u32, ParseError> {
    let age: u32 = input.parse()
        .map_err(|_| ParseError::InvalidFormat)?;
    
    if age > 150 {
        return Err(ParseError::OutOfRange);
    }
    
    Ok(age)
}

パターン 5: from_iter での集約

複数の Result を扱う場合:

fn process_items(items: Vec<String>) -> Result<Vec<i32>, Box<dyn std::error::Error>> {
    items.iter()
        .map(|s| s.parse::<i32>())
        .collect()  // Result<Vec<i32>, ParseIntError>
}
複数のエラーを収集する
let results: Vec<_> = items.iter()
    .map(process)
    .collect();

let (ok, err): (Vec<_>, Vec<_>) = results.into_iter()
    .partition(Result::is_ok);

パターン 6: anyhow クレートでの簡潔化

use anyhow::{Result, Context};

fn complex_operation() -> Result<String> {
    let file_content = std::fs::read_to_string("data.txt")
        .context("Failed to read file")?;
    
    let number: i32 = file_content.trim().parse()
        .context("Failed to parse number")?;
    
    Ok(format!("Got: {}", number))
}

エラーハンドリングはRustプログラム開発の重要な要素です。適切なパターンを選択することで、堅牢で保守性の高いコードが実現できます。