Rustのenumとパターンマッチが強力な理由

プログラミング言語

Rustのenumとパターンマッチは、他言語にはない強力な表現力を持っています。データの「形」と「分岐」を一体化して扱えるため、バグの少ないコードが書きやすくなります。本記事では、Rustのenumとパターンマッチの強さを解説します。

Rustのenumは「タグ付きユニオン」

Rustのenumは、各バリアントが独自のデータを持てます。C言語のenum(単なる定数)とは別物です。

enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(u8, u8, u8),
}

パターンマッチで分岐

fn handle(msg: Message) {
    match msg {
        Message::Quit => println!("quit"),
        Message::Move { x, y } => println!("move to ({}, {})", x, y),
        Message::Write(text) => println!("write: {}", text),
        Message::ChangeColor(r, g, b) => println!("rgb({}, {}, {})", r, g, b),
    }
}

すべてのバリアントを処理する必要があり、漏れがあるとコンパイルエラーになります(網羅性チェック)。新しいバリアントを追加した時の修正漏れが防げます。

OptionとResult

Rustでは「値が無いかも」「エラーが起きるかも」を型で表現します。これがnull安全・例外なしの仕組みです。

// 標準ライブラリの定義
enum Option<T> { Some(T), None }
enum Result<T, E> { Ok(T), Err(E) }

fn find_user(id: u32) -> Option<User> { ... }
fn fetch(url: &str) -> Result<String, Error> { ... }

if let・while let

if let Some(user) = find_user(1) {
    println!("found: {}", user.name);
}

while let Some(item) = stack.pop() {
    process(item);
}

?演算子でエラー伝搬

fn load() -> Result<Config, Error> {
    let text = fs::read_to_string("config.toml")?;  // エラーなら早期return
    let cfg = toml::from_str(&text)?;
    Ok(cfg)
}

パターンの応用

match value {
    0 => "zero",
    1 | 2 => "one or two",
    3..=9 => "single digit",
    n if n < 0 => "negative",
    _ => "other",
}

網羅性の威力

enumに新しいバリアントを追加すると、コンパイラがすべてのmatch箇所で漏れを指摘します。これは大規模リファクタリングや状態追加の安全性を大きく高めます。

まとめ

Rustのenum + パターンマッチは「データの形と分岐をコンパイル時に保証する」表現力を持ちます。Option / Result + ?演算子と組み合わせれば、nullや例外に頼らない安全なコードが書けます。Rustの真価を感じるための重要な機能です。