Rust 從 0 到 1 - 模式 - 可反駁性

 模式有兩種形式:可反駁的(refutable)和 不可反駁的(irrefutable)。

模式有兩種形式:可反駁的(refutable)和 不可反駁的(irrefutable)。能匹配任何可能值的模式是不可反駁的。譬如我們前面舉的例子 let x = 5; 中的 x,因爲 x 可以匹配任何值所以不可能匹配失敗。反之,對於可能的值,存在匹配失敗的情況的模式是可反駁的。譬如,表達式 if let Some(x) = a_value 中的 Some(x),如果變量 a_value 中的值是 None 而不是 Some,那麼與模式 Some(x) 就會匹配失敗。

函數參數、 let 語句和 for 循環只能使用不可反駁的模式,因爲對於不匹配的情況,程序無法執行任何有意義的操作。if let 和 while let 表達式中兩種模式都可以使用,只是在使用不可反駁模式時,編譯器會產生警告,因爲從它們的定義上來說,他們需要處理失敗的情況:條件表達式的功能就是根據匹配成功與否執行不同的操作。

通常情況下我們無需關心可反駁和不可反駁模式的區別,不過我們需要熟悉可反駁性的概念,這樣當我們在錯誤信息中看到相關提示時就知道應該如何處理。當發生這種情況時,我們需要根據實際的使用場景,修改模式或者模式的用法。下面讓我們看看在 Rust 中要求使用不可反駁模式的地方使用可反駁模式以及反過來的情況的簡單例子:

let Some(x) = some_option_value;

如果 some_option_value 的值是 None,則與模式 Some(x) 匹配失敗,也就是說這個模式是可反駁的。而在之前我們說過 let 語句只能接受不可反駁的模式,因爲對於 None,代碼無法執行任何有意義的操作。當我們嘗試在要求不可反駁模式的地方使用可反駁模式時,Rust 會產生類似下面的編譯時錯誤:

$ cargo run
   Compiling patterns v0.1.0 (file:///projects/patterns)
error[E0005]: refutable pattern in local binding: `None` not covered
   --> src/main.rs:3:9
    |
3   |     let Some(x) = some_option_value;
    |         ^^^^^^^ pattern `None` not covered
    |
    = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant
    = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html
    = note: the matched value is of type `Option<i32>`
help: you might want to use `if let` to ignore the variant that isn't matched
    |
3   |     if let Some(x) = some_option_value { /* */ }
    |
error: aborting due to previous error
For more information about this error, try `rustc --explain E0005`.
error: could not compile `patterns`
To learn more, run the command again with --verbose.

因爲我們沒有覆蓋(也無法覆蓋)到模式 Some(x) 的所有可能的值, 所以 Rust 在編譯時會報錯。爲了修復錯誤,我們可以修改模式的用法:使用 if let 替代 let,參考下面的例子:

if let Some(x) = some_option_value {
    println!("{}", x);
}

反之,如果我們在 if let 中使用總是會匹配的模式,即不可反駁模式,會發生什麼呢?參考下面的例子:

if let x = 5 {
    println!("{}", x);
};

嘗試進行編譯,Rust 會產生類似下面的告警(注意,不是錯誤):

warning: irrefutable `if let` pattern
 --> src/main.rs:2:5
  |
2 | /     if let x = 5 {
3 | |         println!("{}", x);
4 | |     };
  | |_____^
  |
  = note: `#[warn(irrefutable_let_patterns)]` on by default
  = note: this pattern will always match, so the `if let` is useless
  = help: consider replacing the `if let` with a `let`
warning: 1 warning emitted
    Finished dev [unoptimized + debuginfo] target(s) in 0.39s
     Running `target/debug/patterns`

同樣的原因,match 分支必須使用可反駁模式,除了最後一個分支可能需要使用能匹配其它任何值的不可反駁模式。Rust 允許我們在只有一個分支的 match 中使用不可反駁模式,但是這個語法並不是太有用,並且也可以被更爲簡單的 let 語句代替。

現在,我們已經知道在哪裏可以使用模式,以及可反駁模式和不可反駁模式的區別,下面我們將介紹可以用於創建模式的語法。

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/554O6-S2pBs_QDA_-QUGrg