// 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プログラム開発の重要な要素です。適切なパターンを選択することで、堅牢で保守性の高いコードが実現できます。