# 模式
> [patterns.md](https://github.com/rust-lang/rust/blob/master/src/doc/book/patterns.md)
commit 6ba952020fbc91bad64be1ea0650bfba52e6aab4
模式在Rust中十分常見。我們在[變量綁定](#),[匹配語句](#)和其它一些地方使用它們。讓我們開始一個快速的關于模式可以干什么的教程!
快速回顧:你可以直接匹配常量,并且`_`作為“任何”類型:
~~~
let x = 1;
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("anything"),
}
~~~
這會打印出`one`。
有一個模式的陷阱:就像任何引入一個新綁定的語句,他們會引入隱藏。例如:
~~~
let x = 1;
let c = 'c';
match c {
x => println!("x: {} c: {}", x, c),
}
println!("x: {}", x)
~~~
這會打印:
~~~
x: c c: c
x: 1
~~~
換句話說,`x =>`匹配到了模式并引入了一個叫做`x`的新綁定。這個新綁定的作用域是匹配分支并擁有`c`的值。注意匹配作用域外的`x`的值對內部的`x`的值并無影響。因為我們已經有了一個`x`,新的`x`隱藏了它。
### 多重模式(Multiple patterns)
你可以使用`|`匹配多個模式:
~~~
let x = 1;
match x {
1 | 2 => println!("one or two"),
3 => println!("three"),
_ => println!("anything"),
}
~~~
這會輸出`one or two`。
### 解構(Destructuring)
如果你有一個復合數據類型,例如一個[結構體](#),你可以在模式中解構它:
~~~
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { x, y } => println!("({},{})", x, y),
}
~~~
我們可以用`:`來給出一個不同的名字:
~~~
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { x: x1, y: y1 } => println!("({},{})", x1, y1),
}
~~~
如果你只關心部分值,我們不需要給它們都命名:
~~~
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { x, .. } => println!("x is {}", x),
}
~~~
這會輸出`x is 0`。
你可以對任何成員進行這樣的匹配,不僅僅是第一個:
~~~
struct Point {
x: i32,
y: i32,
}
let origin = Point { x: 0, y: 0 };
match origin {
Point { y, .. } => println!("y is {}", y),
}
~~~
這會輸出`y is 0`。
這種“解構”行為可以用在任何復合數據類型上,例如[元組](#)和[枚舉](#)
### 忽略綁定(Ignoring bindings)
你可以在模式中使用`_`來忽視它的類型和值。例如,這是一個`Result<T, E>`的`match`:
~~~
# let some_value: Result<i32, &'static str> = Err("There was an error");
match some_value {
Ok(value) => println!("got a value: {}", value),
Err(_) => println!("an error occurred"),
}
~~~
在第一個分支,我們綁定了`Ok`變量中的值為`value`,不過在`Err`分支,我們用`_`來忽視特定的錯誤,而只是打印了一個通用的錯誤信息。
`_`在任何創建綁定的模式中都有效。這在忽略一個大大結構體的部分字段時很有用:
~~~
fn coordinate() -> (i32, i32, i32) {
// generate and return some sort of triple tuple
# (1, 2, 3)
}
let (x, _, z) = coordinate();
~~~
這里,我們綁定元組第一個和最后一個元素為`x`和`z`,不過省略了中間的元素。
相似的,你可以在模式中用`..`來忽略多個值。
~~~
enum OptionalTuple {
Value(i32, i32, i32),
Missing,
}
let x = OptionalTuple::Value(5, -2, 3);
match x {
OptionalTuple::Value(..) => println!("Got a tuple!"),
OptionalTuple::Missing => println!("No such luck."),
}
~~~
這會打印`Got a tuple!`。
### `ref`和`ref mut`
如果你想要一個引用,使用`ref`關鍵字:
~~~
let x = 5;
match x {
ref r => println!("Got a reference to {}", r),
}
~~~
這會輸出`Got a reference to 5`。
這里,`match`中的`r`是`&i32`類型的。換句話說,`ref`關鍵字創建了一個在模式中使用的引用。如果你需要一個可變引用,`ref mut`同樣可以做到:
~~~
let mut x = 5;
match x {
ref mut mr => println!("Got a mutable reference to {}", mr),
}
~~~
### 范圍(Ranges)
你可以用`...`匹配一個范圍的值:
~~~
let x = 1;
match x {
1 ... 5 => println!("one through five"),
_ => println!("anything"),
}
~~~
這會輸出`one through five`。
范圍經常用在整數和`char`上。
~~~
let x = '?';
match x {
'a' ... 'j' => println!("early letter"),
'k' ... 'z' => println!("late letter"),
_ => println!("something else"),
}
~~~
這會輸出`something else`。
### 綁定
你可以使用`@`把值綁定到名字上:
~~~
let x = 1;
match x {
e @ 1 ... 5 => println!("got a range element {}", e),
_ => println!("anything"),
}
~~~
這會輸出`got a range element 1`。在你想對一個復雜數據結構進行部分匹配的時候,這個特性十分有用:
~~~
#[derive(Debug)]
struct Person {
name: Option<String>,
}
let name = "Steve".to_string();
let mut x: Option<Person> = Some(Person { name: Some(name) });
match x {
Some(Person { name: ref a @ Some(_), .. }) => println!("{:?}", a),
_ => {}
}
~~~
這會輸出 `Some("Steve")`,因為我們把Person里面的`name`綁定到`a`。
如果你在使用`|`的同時也使用了`@`,你需要確保名字在每個模式的每一部分都綁定名字:
~~~
let x = 5;
match x {
e @ 1 ... 5 | e @ 8 ... 10 => println!("got a range element {}", e),
_ => println!("anything"),
}
~~~
### 守衛(Guards)
你可以用`if`來引入*匹配守衛*(*match guards*):
~~~
enum OptionalInt {
Value(i32),
Missing,
}
let x = OptionalInt::Value(5);
match x {
OptionalInt::Value(i) if i > 5 => println!("Got an int bigger than five!"),
OptionalInt::Value(..) => println!("Got an int!"),
OptionalInt::Missing => println!("No such luck."),
}
~~~
這會輸出`Got an int!`。
如果你在`if`中使用多重模式,`if`條件將適用于所有模式:
~~~
let x = 4;
let y = false;
match x {
4 | 5 if y => println!("yes"),
_ => println!("no"),
}
~~~
這會打印`no`,因為`if`適用于整個`4 | 5`,而不僅僅是`5`,換句話說,`if`語句的優先級是這樣的:
~~~
(4 | 5) if y => ...
~~~
而不是這樣:
~~~
4 | (5 if y) => ...
~~~
### 混合與匹配(Mix and Match)
(口哨)!根據你的需求,你可以對上面的多種匹配方法進行組合:
~~~
match x {
Foo { x: Some(ref name), y: None } => ...
}
~~~
模式十分強大。好好使用它們。
- 前言
- 貢獻者
- 1.介紹
- 2.準備
- 3.學習 Rust
- 3.1.猜猜看
- 3.2.哲學家就餐問題
- 3.3.其它語言中的 Rust
- 4.語法和語義
- 4.1.變量綁定
- 4.2.函數
- 4.3.原生類型
- 4.4.注釋
- 4.5.If語句
- 4.6.循環
- 4.7.所有權
- 4.8.引用和借用
- 4.9.生命周期
- 4.10.可變性
- 4.11.結構體
- 4.12.枚舉
- 4.13.匹配
- 4.14.模式
- 4.15.方法語法
- 4.16.Vectors
- 4.17.字符串
- 4.18.泛型
- 4.19.Traits
- 4.20.Drop
- 4.21.if let
- 4.22.trait 對象
- 4.23.閉包
- 4.24.通用函數調用語法
- 4.25.crate 和模塊
- 4.26.const和static
- 4.27.屬性
- 4.28.type別名
- 4.29.類型轉換
- 4.30.關聯類型
- 4.31.不定長類型
- 4.32.運算符和重載
- 4.33.Deref強制多態
- 4.34.宏
- 4.35.裸指針
- 4.36.不安全代碼
- 5.高效 Rust
- 5.1.棧和堆
- 5.2.測試
- 5.3.條件編譯
- 5.4.文檔
- 5.5.迭代器
- 5.6.并發
- 5.7.錯誤處理
- 5.8.選擇你的保證
- 5.9.外部函數接口
- 5.10.Borrow 和 AsRef
- 5.11.發布途徑
- 5.12.不使用標準庫
- 6.Rust 開發版
- 6.1.編譯器插件
- 6.2.內聯匯編
- 6.4.固有功能
- 6.5.語言項
- 6.6.鏈接進階
- 6.7.基準測試
- 6.8.裝箱語法和模式
- 6.9.切片模式
- 6.10.關聯常量
- 6.11.自定義內存分配器
- 7.詞匯表
- 8.語法索引
- 9.參考文獻
- 附錄:名詞中英文對照