match 控制流運算符

Rust 提供一種 match 控制流程,可以將一個值與一系列的模式相比較,根據相匹配的結果執行對應的程式碼。

[!IMPORTANT]

match 好用的點來自於編譯器的檢查,編譯器會確保所有可能的情況都會得到處理。

假設有一個函式可以獲取一個未知的美幣,並以類似驗鈔機的功能,判斷他是何種硬幣後返回硬幣的美金幣值。

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter,
}

fn value_in_cents(coin: Coin) -> u32 {
    // 這裡的 coin 為表達式,也是 match 的判斷式
    match coin {
        // 模式 (pattern) => 程式碼
        // 當 coin 符合底下其中一個模式時,就執行 => 後方的程式碼
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

match 中會列出各種模式 (pattern) 與其對應要執行的程式碼。

matchif 的差別在 if 的判斷式要求為 bool 值,但 match 的條件可以是任何類型。

如果需要在符合模式之後執行一段多行的程式碼,可以用大括號包裹多行程式碼。

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => {
            println!("Lucky penny!");
            1
        },
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter => 25,
    }
}

綁定值的模式

match 另一個有用的特點是可以綁定匹配模式的部分值。這也是我們如何從列舉變數中提取數值的方法。

舉個例子,修改 Coin 中的成員 Quarter,使其帶上一個州的資料。

#[derive(Debug)] // 這樣可以立刻看到州的名稱
enum UsState {
    Alabama,
    Alaska,
    // --snip--
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

在用 match 匹配的過程中,我們可以在匹配 Coin::Quarter 的模式中加上一個 state 的參數。

當匹配到 Coin::Quarter 時,state 就會自動綁定 Quarter 設定的值。

#[derive(Debug)]
enum UsState {
    Alabama,
    Alaska,
}

enum Coin {
    Penny,
    Nickel,
    Dime,
    Quarter(UsState),
}

fn value_in_cents(coin: Coin) -> u32 {
    match coin {
        Coin::Penny => 1,
        Coin::Nickel => 5,
        Coin::Dime => 10,
        Coin::Quarter(state) => {
            println!("State quarter from {:?}!", state); // Alaska
            25
        },
    }
}

fn main() {
    value_in_cents(Coin::Quarter(Alaska));
}

匹配 Option

可以使用 match,將 Option 中的 T 給拿出來。

let some_value = Some(1);

let result = match some_value {
    Some(i) => i,
    None => {
        panic!("There is no value");
    }
};

println!("{}", result); // 1

來寫一個函式,它獲取一個 Option<32> 參數,如果有值就將其加一,沒有值就返回 None

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        None => None,
        Some(i) => Some(i + 1),
    }
}

let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);

匹配 Some(T)

在剛剛的操作中,當調用 plus_one(five) 時,plus_one 函式中的 x 值會是 Some(5)

比較第一個模式,很明顯不匹配。

None => None,

比較第二個模式,Some(i)Some(5) 是匹配的,因此執行對應的程式碼。

Some(i) => Some(i + 1),

而調用 plus_one(None) 時,第一個模式就匹配了,所以返回 None

匹配是窮盡的

match 有一個特性需要特別討論。以下方的程式碼為例,這段程式碼是無法執行的。

fn plus_one(x: Option<i32>) -> Option<i32> {
    match x {
        Some(i) => Some(i + 1),
    }
}

原因就在於我們沒有處理 None 的情況,這個時候 Rust 會貼心的提醒我們。

rror[E0004]: non-exhaustive patterns: `None` not covered
 --> src/main.rs:6:11
  |
6 |     match x {
  |           ^ pattern `None` not covered
  |

Rust 知道我們沒有覆蓋到所有可能的情況,甚至知道有哪些情況被我們忽略了,Rust 中的匹配是窮盡 (exhaustive) 的,必須要窮舉所有可能性來使程式碼可以執行

通配符 _

Rust 也提供一個模式 _ 用於囊括所有不符合其他模式的處理方式。

例如 u8 可以擁有 0 到 255 的值,假設我們只關心 1, 2, 3 的值,又不想全部列出 4 到 255 的值,這時候就可以使用 _ 代替。

let some_u8_value = 0u8;

match some_u8_value {
    1 => println!("one"),
    2 => println!("two"),
    3 => println!("three"),
    _ => (),
}

This site uses Just the Docs, a documentation theme for Jekyll.