Ch3:控制流與解構術 — 邏輯的對稱之美
條件判斷:不僅是判斷,還是表達式
在 Rust,if-else 不是單純的語句(Statement),它是一種可以回傳值的表達式(Expression)。這意味著你可以直接把 if 的結果賦值給變數。
let condition = true;
let x = if condition { "成功" } else { "失敗" };
⚡ 進階講解
- 分號是關鍵:括號內最後一行如果不加分號,它就是回傳值。如果加了分號,它就變成語句,回傳的是空元組 ()。
- 型態必須一致:Rust 是強型態語言,if 分支回傳字串,else 分支就不能回傳整數。編譯器必須在編譯時期就知道 x 的精確型態。
- 取代三元運算子:Rust 沒有
a ? b : c這種語法,因為 if 表達式已經完美取代了它,且可讀性更高。
迴圈:三種武器
Rust 提供了三種迴圈,每一種都有其獨特的應用場景。
1. while:最傳統的守衛
當你需要先判斷條件再執行時使用。但在 Rust 中,while 比較少用來遍歷陣列,因為 for 更安全且更快(少了邊界檢查的開銷)。
2. loop:無限的可能
loop 是最純粹的無限迴圈。它最酷的地方在於可以配合 break 回傳值。這在需要重試某些操作(如網路連接或獲取鎖)直到成功為止的場景非常強大。
let mut counter = 0;
let result = loop {
counter += 1;
if counter == 10 {
break counter * 2; // 跳出迴圈並帶走數值 20
}
};
💡 祕技:迴圈標籤 (Loop Labels)
當你有巢狀迴圈時,可以用標籤來指名要跳出哪一層:
'outer: loop {
loop {
if some_condition { break 'outer; } // 直接殺出重圍跳出最外層
}
}
3. for:最推薦的戰力
這是 Rust 最常用的迴圈,通常搭配迭代器。它不會發生「索引越界」的慘劇。
1..3:左閉右開,產生 1, 2。1..=3:全閉區間,產生 1, 2, 3。
解構 (Destructuring):九字箴言
解構是 Rust 的精華,心法只有一句:「怎麼建立,就怎麼解開。」 當你定義了一個結構,解構就是把裡面的組件拆散開來,直接賦值給新變數。
| 資料型態 | 建立方式 | 解構方式 | 核心概念 |
|---|---|---|---|
| Array | [1, 2, 3] |
let [x, y, z] = arr; |
順序與數量必須精確匹配 |
| Tuple | (1, "a") |
let (x, y) = t; |
支援不同型態的一口氣拆解 |
| Struct | Point { x, y } |
let Point { x, y } = p; |
依據欄位名稱拆解,順序不重要 |
⚡ 花式解構技巧
..:代表「剩下的我不管」。例如let [first, ..] = big_array;。_:忽略特定位置的變數,不想取名時好用。@:綁定運算子。這能在解構的同時進行條件判斷並存入變數。
let msg = 15;
match msg {
id @ 10..=20 => println!("ID 在範圍內:{}", id),
_ => println!("在範圍外"),
}
Match:窮舉性判斷
match 是 Rust 的殺手級功能。它不僅是 switch-case 的強化版,還具有**窮舉性(Exhaustiveness)**檢查。編譯器會盯著你,確保你考慮了所有的可能性。
match score {
90..=100 => 'S',
80..90 => 'A',
_ => 'F', // 萬用牌,處理剩下的所有情況
}
⚡ 進階:Match Guard (守衛條件)
你可以在匹配分支中加入 if 條件,這讓邏輯判斷更有彈性:
match pair {
(x, y) if x == y => println!("兩數相等"),
(x, y) if x + y == 0 => println!("互為相反數"),
_ => println!("平凡的組合"),
}
if let / while let:精簡的藝術
當你只關心 Enum 中的某一種情況時,寫 match 顯得太囉唆,這時就是 if let 的主場。
// 如果是 Some,就解構出內部的 x,否則直接跳過
if let Some(x) = foo_opt {
println!("拿到了:{}", x);
}
while let:連續解構
這常用在不斷從容器中取出資料直到空的為止:
let mut stack = vec![1, 2, 3];
while let Some(top) = stack.pop() {
println!("彈出:{}", top);
}
問號運算子 (?):錯誤處理的電梯
Rust 不使用 try-catch 拋出例外,而是回傳 Result。? 運算子就像是一部自動電梯,幫你處理錯誤的向上傳播(Propagation)。
let file = File::open("test.txt")?; // 如果失敗,直接從當前函數 Return Err;如果成功,直接解開賦值。
這能讓你的代碼保持平整,避免出現地獄般的巢狀 match。
函數 (Function)
- fn 宣告。參數必須標註型態,這讓 Rust 能在編譯期進行嚴格檢查。
- 表達式回傳:最後一行不加分號直接回傳。
- ! (Never Type):這是一個特殊的型態,代表「這個函數永遠不會結束」。常見於無限迴圈的伺服器、REPL 或者會觸發
panic!的崩潰函數。
fn dead_end() -> ! {
panic!("此路不通");
}
const fn:編譯期執行
如果你在函數前加上 const,這代表這個函數可以在編譯期間就被執行並算出結果。這對於效能優化非常有幫助,因為它把執行期的工作提前到了編譯期。
const fn add(a: i32, b: i32) -> i32 { a + b }
const RESULT: i32 = add(5, 10); // 編譯完這行就是 15,執行時零開銷