本文 AI 產出,尚未審核

Rust 課程 – 函數與控制流

主題:表達式 vs 陳述式


簡介

在 Rust 中,表達式 (expression)陳述式 (statement) 的差異是語言設計的核心之一。它直接影響程式的可讀性、可組合性以及錯誤處理的方式。對於剛接觸 Rust 的學習者來說,往往會把兩者混為一談,結果寫出來的程式碼既冗長又易出錯。

本篇文章將以 淺顯易懂 的方式說明什麼是表達式、什麼是陳述式,並透過實作範例展示它們在 函式、條件判斷、迴圈 等控制流結構中的實務運用。掌握這個概念後,你將能寫出更具 表達力、更安全且更符合 Rust 風格的程式碼。


核心概念

1. 基本定義

項目 說明
表達式 產生一個值,且在語法上可以被「放」在其他表達式裡。例如 5 + 3some_vec.len()if condition { 1 } else { 0 }
陳述式 執行某個動作,但不會直接產生可用的值。常見的有變數宣告、letreturnloopwhileforbreakcontinue 等。

重點:在 Rust 中,幾乎所有程式碼都是 表達式,只有少數關鍵字會形成 陳述式。這讓語言在編譯期就能進行更嚴格的型別推斷與所有權檢查。


2. 為什麼表達式很重要?

  1. 函式回傳值:Rust 的函式最後一行如果是表達式,就會自動成為回傳值,省去 return 關鍵字。
  2. 組合子程式:表達式可以直接嵌套在其他表達式裡,形成簡潔的「鏈式」寫法。
  3. 所有權與借用:表達式的結果會遵循所有權規則,讓編譯器在編譯階段就捕捉到潛在的錯誤。

3. 常見的表達式類型

類型 範例 說明
字面值 42true"hello" 直接產生固定值。
運算子表達式 a + b * c 依照運算子優先權計算後產生值。
函式呼叫 std::mem::size_of::<i32>() 呼叫函式後取得回傳值。
區塊表達式 { let x = 5; x * 2 } 大括號內的最後一個表達式成為區塊的結果。
條件表達式 if cond { 1 } else { 0 } if/else 可以是表達式,必須保證兩支分支回傳同型別。
迭代表達式 `vec.iter().map( x

4. 陳述式的角色

  • 變數綁定let x = 5;(宣告並綁定)
  • 流程控制if, match, loop, while, for(雖然 if/match 也能是表達式,但作為控制流時屬於陳述式)
  • 返回return expr;(強制提前返回)
  • 宏展開println!("Hello");(宏本身是陳述式)

提示:在需要「副作用」而非「值」的情境下,使用陳述式會讓意圖更清晰。


程式碼範例

以下示範 5 個常見情境,說明表達式與陳述式的差異與最佳寫法。

範例 1:函式回傳值的表達式寫法

// 使用最後一行的表達式自動回傳
fn max(a: i32, b: i32) -> i32 {
    if a > b { a } else { b }   // <-- 這裡是表達式,直接成為回傳值
}

說明if 這裡是 表達式,兩個分支都回傳 i32,因此函式不需要 return


範例 2:區塊表達式與所有權

fn create_string() -> String {
    // 大括號內是區塊表達式,最後一行的 String 會被移動 (move) 出去
    {
        let s = String::from("Rust");
        s   // 這裡是表達式,返回 s 的所有權
    }
}

說明:區塊本身是表達式,裡面的 let 是陳述式。最後一行 s 把所有權交給呼叫端。


範例 3:使用 match 作為表達式

enum Shape {
    Circle(f64),
    Rectangle { w: f64, h: f64 },
}

fn area(shape: Shape) -> f64 {
    match shape {
        Shape::Circle(r) => std::f64::consts::PI * r * r,
        Shape::Rectangle { w, h } => w * h,
    } // `match` 產生 f64,直接回傳
}

說明match 每個分支都回傳同型別 (f64),因此整個 match 成為表達式。


範例 4:迴圈中的 break 帶值(表達式)

fn find_first_even(nums: &[i32]) -> Option<i32> {
    for &n in nums {
        if n % 2 == 0 {
            break Some(n);   // `break` 可以帶值,成為迴圈的結果表達式
        }
    }
    None
}

說明break Some(n)Some(n) 作為迴圈的回傳值,for 迴圈本身是表達式。


範例 5:從陳述式到表達式的改寫

// 陳述式寫法
fn sum_until(limit: i32) -> i32 {
    let mut sum = 0;
    let mut i = 0;
    while i < limit {
        sum += i;
        i += 1;
    }
    sum   // 最後的 `sum` 是表達式,作為回傳值
}

// 更 idiomatic 的表達式寫法
fn sum_until(limit: i32) -> i32 {
    (0..limit).fold(0, |acc, x| acc + x)   // 完全使用表達式
}

說明:第二個版本把 while 迴圈(陳述式)改寫成 fold(純表達式),更符合 Rust 的函式式風格。


常見陷阱與最佳實踐

陷阱 可能的結果 建議的解決方式
忘記在區塊最後加分號 區塊會返回最後一行的值,導致型別不符合預期 若不想回傳值,在最後一行加分號 (;) 讓它變成陳述式
if / match 分支型別不一致 編譯錯誤 mismatched types 確保所有分支回傳同一型別,或使用 () 作為單位型別
在需要副作用的地方使用表達式 程式可讀性下降,容易忽略副作用 明確使用陳述式(如 let _ = expr;)或加入註解說明
過度使用 return 失去 Rust 「最後一行即回傳」的簡潔性 盡量讓函式最後一行成為表達式,除非需要提前返回
while/for 內直接 break 帶值 初學者可能不熟悉此語法 先熟悉基本迴圈,再逐步使用 break value 來返回結果

最佳實踐

  1. 優先使用表達式:讓程式碼更具宣告性與可組合性。
  2. 保持型別一致:尤其在 ifmatchloop 等表達式中。
  3. 利用區塊表達式:在需要臨時變數但又想回傳結果時,使用 { … } 包住程式碼。
  4. 適度加入註解:對於看起來不直觀的表達式(例如 break Some(v)),加上說明可提升可讀性。

實際應用場景

1. 配置檔解析

在解析 JSON、YAML 等設定檔時,常會根據不同欄位組合出不同的結構。利用 match 表達式可以一次完成 檢查 + 建構,避免多層 if 陳述式。

fn parse_mode(mode: &str) -> Result<Mode, &'static str> {
    match mode {
        "debug" => Ok(Mode::Debug),
        "release" => Ok(Mode::Release),
        _ => Err("未知的模式"),
    }
}

2. 錯誤處理(Result)的鏈式寫法

Result 本身是表達式,配合 ? 操作子可讓錯誤傳遞變得非常簡潔。

fn read_file(path: &str) -> std::io::Result<String> {
    let mut file = std::fs::File::open(path)?;   // 陳述式,返回 Result
    let mut contents = String::new();
    file.read_to_string(&mut contents)?;        // 仍是陳述式
    Ok(contents)                                 // 表達式,作為回傳值
}

3. 生成測試資料

在單元測試中,常會使用 區塊表達式 產生臨時資料,同時保證所有權正確。

#[test]
fn test_sum() {
    let data = {
        let mut v = Vec::new();
        for i in 1..=5 { v.push(i); }
        v   // 這裡返回 Vec<i32>
    };
    assert_eq!(data.iter().sum::<i32>(), 15);
}

總結

  • 表達式 產生值,可嵌入其他表達式;陳述式 執行動作,通常不返回值。
  • Rust 的設計讓 大部分程式碼都是表達式,這使得函式、控制流、所有權檢查都能在編譯期得到嚴格保證。
  • 熟練 區塊表達式if/match 作為表達式、break 帶值等技巧,能寫出更簡潔、可讀且安全的程式。
  • 在實務開發中,將配置解析、錯誤處理、測試資料生成等情境轉換為表達式風格,能顯著提升程式碼品質與維護效率。

掌握了「表達式 vs 陳述式」的差異與使用時機,你就能在 Rust 中更自然地運用 函式式編程 的優雅,同時保持 系統層級的安全性。祝你在 Rust 的旅程中寫出更好、更安全的程式碼!