列舉與模式比對
列舉(enum)表示一個值「可能是幾種情況之一」,每種情況稱為一個變體(variant),而且變體可以攜帶資料。搭配 match,編譯器會確保你處理了每一種情況。這是 Rust 最好用的建模工具之一,Option 與 Result 也都是列舉。
定義列舉
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>:
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 章製作,涵蓋 enum、Option、match 與 if let,對應本頁全部內容。
下一頁:字串與集合。