跳轉至

建立 2026-09-19 更新 2026-09-19

列舉與模式比對

列舉(enum)表示一個值「可能是幾種情況之一」,每種情況稱為一個變體(variant),而且變體可以攜帶資料。搭配 match,編譯器會確保你處理了每一種情況。這是 Rust 最好用的建模工具之一,OptionResult 也都是列舉。

定義列舉

enum Direction {
    North,
    South,
    East,
    West,
}

fn main() {
    let d = Direction::North;
    match d {
        Direction::North => println!("往北"),
        Direction::South => println!("往南"),
        Direction::East => println!("往東"),
        Direction::West => println!("往西"),
    }
}

變體可以帶資料

每個變體可以攜帶不同型別與數量的資料,這讓它比其他語言的列舉強大得多:

enum Message {
    Quit,                       // 不帶資料
    Move { x: i32, y: i32 },    // 具名欄位
    Write(String),              // 一個 String
    ChangeColor(u8, u8, u8),    // 三個 u8
}

impl Message {
    fn describe(&self) -> String {
        match self {
            Message::Quit => String::from("離開"),
            Message::Move { x, y } => format!("移動到 ({x}, {y})"),
            Message::Write(text) => format!("寫入:{text}"),
            Message::ChangeColor(r, g, b) => format!("換色 rgb({r}, {g}, {b})"),
        }
    }
}

fn main() {
    let msgs = [
        Message::Quit,
        Message::Move { x: 1, y: 2 },
        Message::Write(String::from("hi")),
        Message::ChangeColor(255, 0, 0),
    ];
    for m in &msgs {
        println!("{}", m.describe());
    }
}

Option:Rust 沒有 null

許多語言用 null 表示「沒有值」,卻也因此留下大量空指標錯誤。Rust 沒有 null,改用標準函式庫的列舉 Option<T>

enum Option<T> {
    None,      // 沒有值
    Some(T),   // 有一個 T 型別的值
}

Option<i32>i32不同的型別,你必須先處理「可能沒有值」的情況,才能取出裡面的值,所以不會不小心對空值做運算:

fn find_first_even(nums: &[i32]) -> Option<i32> {
    for &n in nums {
        if n % 2 == 0 {
            return Some(n);
        }
    }
    None
}

fn main() {
    match find_first_even(&[1, 3, 4, 7]) {
        Some(n) => println!("找到 {n}"),
        None => println!("沒有偶數"),
    }
}

match 的規則

  • 必須窮舉:漏掉任一個變體就無法編譯。日後新增變體時,編譯器會告訴你哪些 match 需要更新。
  • 可綁定值:像上面的 Some(n),把裡面的值取出並命名為 n
  • 萬用分支:用 _ 涵蓋其餘所有情況,或用變數名稱接住其餘的值。
  • 可加條件(guard):在模式後加 if 進一步限制。
fn classify(n: i32) -> &'static str {
    match n {
        0 => "零",
        x if x < 0 => "負數",
        1..=9 => "個位數",
        _ => "很大的正數",
    }
}

fn main() {
    for n in [-5, 0, 7, 100] {
        println!("{n}:{}", classify(n));
    }
}

if let 與 let else

只關心其中一種情況時,match 寫起來太冗長,可以改用 if let

fn main() {
    let config: Option<u32> = Some(3);

    if let Some(max) = config {
        println!("最大值是 {max}");
    }
}

let else 則適合「不符合就提早離開」的情境,讓主要邏輯不必縮排:

fn describe(input: Option<i32>) -> String {
    let Some(n) = input else {
        return String::from("沒有值");
    };
    format!("值是 {n}")
}

fn main() {
    println!("{}", describe(Some(5)));
    println!("{}", describe(None));
}

實用心法

  • 想表達「幾種可能」時,先想到 enum,而不是用一堆布林旗標或特殊值(如 -1)。
  • 表示「可能沒有」用 Option,表示「可能失敗」用 Result,見 錯誤處理
  • 讓非法狀態無法表示:把不可能同時成立的狀態設計成不同變體。

推薦影音

列舉

簡述:微軟《Beginner's Series to Rust》第 17 集,說明 enum 的定義與使用。

列舉與模式比對

簡述:Let's Get Rusty 依照官方書籍第 6 章製作,涵蓋 enumOptionmatchif let,對應本頁全部內容。

下一頁:字串與集合