解構列舉

就像元組,結構體和列舉也可透過配對來解構:

結構體

struct Foo {
    x: (u32, u32),
    y: u32,
}

#[rustfmt::skip]
fn main() {
    let foo = Foo { x: (1, 2), y: 3 };
    match foo {
        Foo { x: (1, b), y } => println!("x.0 = 1, b = {b}, y = {y}"),
        Foo { y: 2, x: i }   => println!("y = 2, x = {i:?}"),
        Foo { y, .. }        => println!("y = {y}, other fields were ignored"),
    }
}

列舉

模式也可用來將變數綁定至值的某些部分。您可以透過這個方式檢查型別的結構。首先從簡單的 enum 型別開始吧:

enum Result {
    Ok(i32),
    Err(String),
}

fn divide_in_two(n: i32) -> Result {
    if n % 2 == 0 {
        Result::Ok(n / 2)
    } else {
        Result::Err(format!("cannot divide {n} into two equal parts"))
    }
}

fn main() {
    let n = 100;
    match divide_in_two(n) {
        Result::Ok(half) => println!("{n} divided in two is {half}"),
        Result::Err(msg) => println!("sorry, an error happened: {msg}"),
    }
}

這裡我們利用分支來「解構」Result 值。在第一個分支中,half 會與 Ok 變體中的值綁定。在第二個分支中,msg 會綁定至錯誤訊息。

This slide should take about 8 minutes.

結構體

  • 請變更 foo 中的常值,與其他模式配對。
  • Foo 中新增一個欄位,並視需要變更模式。
  • 捕獲和常數運算式之間的區別可能不容易發現。請嘗試將第二個分支的 2 變更為變數,您會發現它幾乎無法運作。現在將其變更為 const,您會看到它再次運作。

列舉

重要須知:

  • if/else 運算式會傳回列舉,之後列舉會透過 match 解除封裝。
  • 您可以嘗試在列舉定義中加入第三個變體,並在執行程式碼時顯示錯誤。請向學員指出程式碼現在有哪些地方還不詳盡,並說明編譯器會如何嘗試給予提示。
  • The values in the enum variants can only be accessed after being pattern matched.
  • Demonstrate what happens when the search is inexhaustive. Note the advantage the Rust compiler provides by confirming when all cases are handled.
  • divide_in_two 的結果儲存在 result 變數中,並在迴圈中 match 結果。由於配對符合時會耗用 msg,因此這麼做並不會執行編譯。如要修正此問題,請配對 &result,而非 result。這會讓 msg 成為參照,因此就不會遭到耗用。這個「人因工程學的配對」功能已於 Rust 2018 推出。如要支援舊版 Rust,請在模式中將 msg 替換成 ref msg