Rust 學習筆記:列舉與模式配對
2023年10月9日 · 中文
前言
本系列文書寫我個人初探 Rust 的學習筆記,章節劃分主要基於著名的 The Book,The Rust Programming Language,程式碼部分通常是個人閱讀消化後以方便說明的方式撰寫,完整學習建議直接參見該書。
The Rust Programming Language
該書也有中文翻譯版,不過個人閱讀以英文原版為主以鞏固對 terminology 的一致認識,我認為對未來閱讀以及查找資料會較為順暢。
不論語言該書都是相當優秀的學習資源,選擇適合你的語言開始學習 Rust 吧。
Appendix F: Translations of the Book
列舉 Enums
enum 定義的是可能的一組特定數值,以下定義一組顏色。
enum Color { Green, Red, Blue } struct Tag { name: String, color: Color }
使用時則是使用 ::
來取得特定顏色。
let tag1 = Tag { name: String::from("new"), color: Color::Blue };
enum 中的 variants 也能夠擁有數值,以下讓 Color
都能有擁有一個 String
。
enum Color { Green(String), Red(String), Blue(String) } fn main() { let c1 = Color::Blue(String::from("Blue")); }
這種做法可以讓 enum 中不同的 variant 有不同的值。
enum Color { Green(String), Red(i32), Blue(f64) } fn main() { let c1 = Color::Green(String::from("Blue")); let c2 = Color::Red(5); let c3 = Color::Blue(1.0); }
enum 像 struct 一樣可以有 impl block 定義 methods。
#[derive(Debug)] enum Color { Green(String), Red(String), Blue(String) } impl Color { fn print(&self) { println!("{:?}", self) } } fn main() { let c1 = Color::Blue(String::from("Blue")); c1.print(); // Blue("Blue") }
Option
Option
是一種內建在 Rust 的 standard library 的 enum,他是 Rust 處理值可能為空值的解決方案,因為 Rust 不像其他許多語言有空值的型別,如 Javascript 的 null
或 undefined。
以下是 Option
的定義:
enum Option<T> { None, Some(T), }
看起來非常的 self explained,Option 代表的是可能為空 None
、或可能有某些數值 Some(T)
。這邊的 T 是 generic,也就是泛型,之後會學習到。
Result
還有另一種特定的 enum 叫做 Result
,與 Option
有些類似,這是他的定義:
enum Result<T, E> { Ok(T), Err(E), }
在某些可能會失敗的函式中會使用 Result
作為回傳值 (比如讀取檔案),讓我們 handle success 和 error case。
模式配對 Pattern matching
Pattern matching 是讓 Rust 的 enum 真正強大的地方,它是一種流程控制,有點像其他語言中的 switch case。
下面的程式是一個簡短的 pattern matching 的範例,我們使用 match
進行 pattern matching,為每個 variant 配對一段 expression,l
在進入 match
之後比對各個 variant 然後執行配對到的程式。
enum Language { Chinese, English, Japanese } fn main() { let l = Language::Japanese; match l { Language::Chinese => println!("你好"), Language::English => println!("Hello"), Language::Japanese => println!("こんにちは") }; // output: こんにちは }
當 enum 有綁定值時可以使用以下方式取得值。
enum Language { Chinese(String), English(String), Japanese(String) } fn main() { let l = Language::English(String::from("Josh")); match l { Language::Chinese(s) => println!("你好 {}", s), Language::English(s) => println!("Hello {}", s), Language::Japanese(s) => println!("こんにちは {}", s) }; // output: Hello Josh }
pattern matching 必須涵蓋所有可能的 variant,若有缺少會直接報錯。
enum Language { Chinese(String), English(String), Japanese(String) } fn main() { let l = Language::English(String::from("Josh")); match l { Language::Chinese(s) => println!("你好 {}", s), Language::English(s) => println!("Hello {}", s), }; // ERROR: non-exhaustive patterns: `Language::Japanese(_)` not covered }
如果只關心部分的可能性,又不想把全部列出來,可以使用 other
變數做 catch-all 的操作。
#[derive(Debug)] enum Language { Chinese(String), English(String), Japanese(String) } fn main() { let l = Language::English(String::from("Josh")); match l { Language::Chinese(s) => println!("你好 {}", s), other => println!("{:?}", other) }; // output: English("Josh") }
如果也不需要取得變數,可以用 _
替代 other
。
enum Language { Chinese(String), English(String), Japanese(String) } fn main() { let l = Language::English(String::from("Josh")); match l { Language::Chinese(s) => println!("你好 {}", s), _ => println!("Hello") }; }
甚至如果只關心部分的可能而其他想要直接忽略,可以用空的 tuple ()
,這樣就什麼都不會發生。
enum Language { Chinese(String), English(String), Japanese(String) } fn main() { let l = Language::English(String::from("Josh")); match l { Language::Chinese(s) => println!("你好 {}", s), _ => () }; }
像上面這種情況可以用 if let
語法做更精簡的改寫。
if let
當我們只關心其中一種情況,其他所有情況都想忽略時,if let
語法提供了更簡單的寫法。
enum Language { Chinese(String), English(String), Japanese(String) } fn main() { let l = Language::English(String::from("Josh")); if let Language::English(s) = l { println!("Hello {}", s); } // output: Hello Josh }
就像單純的 if
一樣,if let
也可以加上 else
區塊,就相當於 pattern matching 中 _
的區塊。
enum Language { Chinese(String), English(String), Japanese(String) } fn main() { let l = Language::Chinese(String::from("Josh")); if let Language::English(s) = l { println!("Hello {}", s); } else { println!("Hello world!"); } // // output: Hello world! }
寫在最後
The book 中下一章是關於套件管理、程式碼拆分與管理專案結構,這部分很重要不過我習慣在實戰中慢慢邊參考邊練習,這邊稍微去閱讀就好,筆記會跳過這章下一篇直接進入集合。