Rust 所有可能會(huì)用到模式的位置

2023-03-22 15:15 更新
ch18-01-all-the-places-for-patterns.md
commit 8a1aad812b90126974853f80d9217e07bd226650

模式出現(xiàn)在 Rust 的很多地方。你已經(jīng)在不經(jīng)意間使用了很多模式!本部分是一個(gè)所有有效模式位置的參考。

match 分支

如第六章所討論的,一個(gè)模式常用的位置是 match 表達(dá)式的分支。在形式上 match 表達(dá)式由 match 關(guān)鍵字、用于匹配的值和一個(gè)或多個(gè)分支構(gòu)成,這些分支包含一個(gè)模式和在值匹配分支的模式時(shí)運(yùn)行的表達(dá)式:

match VALUE {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
}

match 表達(dá)式必須是 窮盡exhaustive)的,意為 match 表達(dá)式所有可能的值都必須被考慮到。一個(gè)確保覆蓋每個(gè)可能值的方法是在最后一個(gè)分支使用捕獲所有的模式:比如,一個(gè)匹配任何值的名稱永遠(yuǎn)也不會(huì)失敗,因此可以覆蓋所有匹配剩下的情況。

有一個(gè)特定的模式 _ 可以匹配所有情況,不過它從不綁定任何變量。這在例如希望忽略任何未指定值的情況很有用。本章之后的 “忽略模式中的值” 部分會(huì)詳細(xì)介紹 _ 模式的更多細(xì)節(jié)。

if let 條件表達(dá)式

第六章討論過了 if let 表達(dá)式,以及它是如何主要用于編寫等同于只關(guān)心一個(gè)情況的 match 語(yǔ)句簡(jiǎn)寫的。if let 可以對(duì)應(yīng)一個(gè)可選的帶有代碼的 else 在 if let 中的模式不匹配時(shí)運(yùn)行。

示例 18-1 展示了也可以組合并匹配 if let、else if 和 else if let 表達(dá)式。這相比 match 表達(dá)式一次只能將一個(gè)值與模式比較提供了更多靈活性;一系列 if let、else if、else if let 分支并不要求其條件相互關(guān)聯(lián)。

示例 18-1 中的代碼展示了一系列針對(duì)不同條件的檢查來(lái)決定背景顏色應(yīng)該是什么。為了達(dá)到這個(gè)例子的目的,我們創(chuàng)建了硬編碼值的變量,在真實(shí)程序中則可能由詢問用戶獲得。

文件名: src/main.rs

fn main() {
    let favorite_color: Option<&str> = None;
    let is_tuesday = false;
    let age: Result<u8, _> = "34".parse();

    if let Some(color) = favorite_color {
        println!("Using your favorite color, {}, as the background", color);
    } else if is_tuesday {
        println!("Tuesday is green day!");
    } else if let Ok(age) = age {
        if age > 30 {
            println!("Using purple as the background color");
        } else {
            println!("Using orange as the background color");
        }
    } else {
        println!("Using blue as the background color");
    }
}

示例 18-1: 結(jié)合 if let、else if、else if let 以及 else

如果用戶指定了中意的顏色,將使用其作為背景顏色。如果今天是星期二,背景顏色將是綠色。如果用戶指定了他們的年齡字符串并能夠成功將其解析為數(shù)字的話,我們將根據(jù)這個(gè)數(shù)字使用紫色或者橙色。最后,如果沒有一個(gè)條件符合,背景顏色將是藍(lán)色:

這個(gè)條件結(jié)構(gòu)允許我們支持復(fù)雜的需求。使用這里硬編碼的值,例子會(huì)打印出 Using purple as the background color。

注意 if let 也可以像 match 分支那樣引入覆蓋變量:if let Ok(age) = age 引入了一個(gè)新的覆蓋變量 age,它包含 Ok 成員中的值。這意味著 if age > 30 條件需要位于這個(gè)代碼塊內(nèi)部;不能將兩個(gè)條件組合為 if let Ok(age) = age && age > 30,因?yàn)槲覀兿Mc 30 進(jìn)行比較的被覆蓋的 age 直到大括號(hào)開始的新作用域才是有效的。

if let 表達(dá)式的缺點(diǎn)在于其窮盡性沒有為編譯器所檢查,而 match 表達(dá)式則檢查了。如果去掉最后的 else 塊而遺漏處理一些情況,編譯器也不會(huì)警告這類可能的邏輯錯(cuò)誤。

while let 條件循環(huán)

一個(gè)與 if let 結(jié)構(gòu)類似的是 while let 條件循環(huán),它允許只要模式匹配就一直進(jìn)行 while 循環(huán)。示例 18-2 展示了一個(gè)使用 while let 的例子,它使用 vector 作為棧并以先進(jìn)后出的方式打印出 vector 中的值:

    let mut stack = Vec::new();

    stack.push(1);
    stack.push(2);
    stack.push(3);

    while let Some(top) = stack.pop() {
        println!("{}", top);
    }

列表 18-2: 使用 while let 循環(huán)只要 stack.pop() 返回 Some 就打印出其值

這個(gè)例子會(huì)打印出 3、2 接著是 1。pop 方法取出 vector 的最后一個(gè)元素并返回 Some(value)。如果 vector 是空的,它返回 None。while 循環(huán)只要 pop 返回 Some 就會(huì)一直運(yùn)行其塊中的代碼。一旦其返回 None,while 循環(huán)停止。我們可以使用 while let 來(lái)彈出棧中的每一個(gè)元素。

for 循環(huán)

如同第三章所講的,for 循環(huán)是 Rust 中最常見的循環(huán)結(jié)構(gòu),不過還沒有講到的是 for 可以獲取一個(gè)模式。在 for 循環(huán)中,模式是 for 關(guān)鍵字直接跟隨的值,正如 for x in y 中的 x。

示例 18-3 中展示了如何使用 for 循環(huán)來(lái)解構(gòu),或拆開一個(gè)元組作為 for 循環(huán)的一部分:

    let v = vec!['a', 'b', 'c'];

    for (index, value) in v.iter().enumerate() {
        println!("{} is at index {}", value, index);
    }

列表 18-3: 在 for 循環(huán)中使用模式來(lái)解構(gòu)元組

示例 18-3 的代碼會(huì)打印出:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
    Finished dev [unoptimized + debuginfo] target(s) in 0.52s
     Running `target/debug/patterns`
a is at index 0
b is at index 1
c is at index 2

這里使用 enumerate 方法適配一個(gè)迭代器來(lái)產(chǎn)生一個(gè)值和其在迭代器中的索引,他們位于一個(gè)元組中。第一個(gè)產(chǎn)生的值是元組 (0, 'a')。當(dāng)這個(gè)值匹配模式 (index, value),index 將會(huì)是 0 而 value 將會(huì)是 'a',并打印出第一行輸出。

let 語(yǔ)句

在本章之前,我們只明確的討論過通過 match 和 if let 使用模式,不過事實(shí)上也在別的地方使用過模式,包括 let 語(yǔ)句。例如,考慮一下這個(gè)直白的 let 變量賦值:

let x = 5;

本書進(jìn)行了不下百次這樣的操作,不過你可能沒有發(fā)覺,這正是在使用模式!let 語(yǔ)句更為正式的樣子如下:

let PATTERN = EXPRESSION;

像 let x = 5; 這樣的語(yǔ)句中變量名位于 PATTERN 位置,變量名不過是形式特別樸素的模式。我們將表達(dá)式與模式比較,并為任何找到的名稱賦值。所以例如 let x = 5; 的情況,x 是一個(gè)代表 “將匹配到的值綁定到變量 x” 的模式。同時(shí)因?yàn)槊Q x 是整個(gè)模式,這個(gè)模式實(shí)際上等于 “將任何值綁定到變量 x,不管值是什么”。

為了更清楚的理解 let 的模式匹配方面的內(nèi)容,考慮示例 18-4 中使用 let 和模式解構(gòu)一個(gè)元組:

    let (x, y, z) = (1, 2, 3);

示例 18-4: 使用模式解構(gòu)元組并一次創(chuàng)建三個(gè)變量

這里將一個(gè)元組與模式匹配。Rust 會(huì)比較值 (1, 2, 3) 與模式 (x, y, z) 并發(fā)現(xiàn)此值匹配這個(gè)模式。在這個(gè)例子中,將會(huì)把 1 綁定到 x,2 綁定到 y 并將 3 綁定到 z。你可以將這個(gè)元組模式看作是將三個(gè)獨(dú)立的變量模式結(jié)合在一起。

如果模式中元素的數(shù)量不匹配元組中元素的數(shù)量,則整個(gè)類型不匹配,并會(huì)得到一個(gè)編譯時(shí)錯(cuò)誤。例如,示例 18-5 展示了嘗試用兩個(gè)變量解構(gòu)三個(gè)元素的元組,這是不行的:

    let (x, y) = (1, 2, 3);

示例 18-5: 一個(gè)錯(cuò)誤的模式結(jié)構(gòu),其中變量的數(shù)量不符合元組中元素的數(shù)量

嘗試編譯這段代碼會(huì)給出如下類型錯(cuò)誤:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0308]: mismatched types
 --> src/main.rs:2:9
  |
2 |     let (x, y) = (1, 2, 3);
  |         ^^^^^^   --------- this expression has type `({integer}, {integer}, {integer})`
  |         |
  |         expected a tuple with 3 elements, found one with 2 elements
  |
  = note: expected tuple `({integer}, {integer}, {integer})`
             found tuple `(_, _)`

For more information about this error, try `rustc --explain E0308`.
error: could not compile `patterns` due to previous error

如果希望忽略元組中一個(gè)或多個(gè)值,也可以使用 _ 或 ..,如 “忽略模式中的值” 部分所示。如果問題是模式中有太多的變量,則解決方法是通過去掉變量使得變量數(shù)與元組中元素?cái)?shù)相等。

函數(shù)參數(shù)

函數(shù)參數(shù)也可以是模式。列表 18-6 中的代碼聲明了一個(gè)叫做 foo 的函數(shù),它獲取一個(gè) i32 類型的參數(shù) x,現(xiàn)在這看起來(lái)應(yīng)該很熟悉:

fn foo(x: i32) {
    // code goes here
}

列表 18-6: 在參數(shù)中使用模式的函數(shù)簽名

x 部分就是一個(gè)模式!類似于之前對(duì) let 所做的,可以在函數(shù)參數(shù)中匹配元組。列表 18-7 將傳遞給函數(shù)的元組拆分為值:

文件名: src/main.rs

fn print_coordinates(&(x, y): &(i32, i32)) {
    println!("Current location: ({}, {})", x, y);
}

fn main() {
    let point = (3, 5);
    print_coordinates(&point);
}

列表 18-7: 一個(gè)在參數(shù)中解構(gòu)元組的函數(shù)

這會(huì)打印出 Current location: (3, 5)。值 &(3, 5) 會(huì)匹配模式 &(x, y),如此 x 得到了值 3,而 y得到了值 5。

因?yàn)槿绲谑滤v閉包類似于函數(shù),也可以在閉包參數(shù)列表中使用模式。

現(xiàn)在我們見過了很多使用模式的方式了,不過模式在每個(gè)使用它的地方并不以相同的方式工作;在一些地方,模式必須是 irrefutable 的,意味著他們必須匹配所提供的任何值。在另一些情況,他們則可以是 refutable 的。接下來(lái)讓我們討論這兩個(gè)概念。


以上內(nèi)容是否對(duì)您有幫助:
在線筆記
App下載
App下載

掃描二維碼

下載編程獅App

公眾號(hào)
微信公眾號(hào)

編程獅公眾號(hào)