|
|
@ -27,11 +27,26 @@ fn main() {
|
|
|
|
- **X | Y**, 是逻辑运算符`或`,代表该分支可以匹配`X`也可以匹配`Y`,只要满足一个即可
|
|
|
|
- **X | Y**, 是逻辑运算符`或`,代表该分支可以匹配`X`也可以匹配`Y`,只要满足一个即可
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
其实`match`跟其他语言中的`switch`非常像,例如`_`类似`switch`中的`default`。
|
|
|
|
其实`match`跟其他语言中的`switch`非常像, 而且`_`类似`switch`中的`default`。
|
|
|
|
|
|
|
|
|
|
|
|
## `match`匹配
|
|
|
|
## `match`匹配
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
首先来看看`match`的通用形式:
|
|
|
|
|
|
|
|
```rust
|
|
|
|
|
|
|
|
match target {
|
|
|
|
|
|
|
|
模式1 => 表达式1,
|
|
|
|
|
|
|
|
模式2 => {
|
|
|
|
|
|
|
|
语句1;
|
|
|
|
|
|
|
|
语句2;
|
|
|
|
|
|
|
|
表达式2;
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
_ => 表达式3
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
该形式清晰的说明了何为模式,何为模式匹配:将模式与`target`进行匹配,即为模式匹配,而模式匹配不仅仅局限于`match`,后面我们会详细阐述。
|
|
|
|
|
|
|
|
|
|
|
|
`match`允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行对应的代码,下面让我们来一一详解,先看一个例子:
|
|
|
|
`match`允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行对应的代码,下面让我们来一一详解,先看一个例子:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
enum Coin {
|
|
|
|
enum Coin {
|
|
|
|
Penny,
|
|
|
|
Penny,
|
|
|
@ -53,11 +68,11 @@ fn value_in_cents(coin: Coin) -> u8 {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
`value_in_cents`函数根据匹配到的硬币类似,返回对应的美分数值,`match`后紧跟着的是一个表达式,跟`if`很像,但是`if`后的表达式必须是一个布尔值,而`match`后的表达式返回值可以是任意类型,只要能跟后面的分支匹配起来即可,这里的`coin`是枚举`Coin`类型。
|
|
|
|
`value_in_cents`函数根据匹配到的硬币类似,返回对应的美分数值,`match`后紧跟着的是一个表达式,跟`if`很像,但是`if`后的表达式必须是一个布尔值,而`match`后的表达式返回值可以是任意类型,只要能跟后面的分支中的模式匹配起来即可,这里的`coin`是枚举`Coin`类型。
|
|
|
|
|
|
|
|
|
|
|
|
接下来是`match`的分支。一个分支有两个部分:一个模式和针对该模式的处理代码。第一个分支的模式是`Coin::Penny`而之后的`=>`运算符将模式和将要运行的代码分开。这里的代码就仅仅是表达式`1`, 不同分支之间使用逗号分隔。
|
|
|
|
接下来是`match`的分支。一个分支有两个部分:**一个模式和针对该模式的处理代码**。第一个分支的模式是`Coin::Penny`而之后的`=>`运算符将模式和将要运行的代码分开。这里的代码就仅仅是表达式`1`, 不同分支之间使用逗号分隔。
|
|
|
|
|
|
|
|
|
|
|
|
当`match`表达式执行时,它将目标值`coin`按顺序与每一个分支的模式相比较, 如果模式匹配了这个值,那么模式之后的代码将被执行。如果模式并不匹配这个值,将继续执行下一个分支。
|
|
|
|
当`match`表达式执行时,它将目标值`coin`按顺序依次与每一个分支的模式相比较, 如果模式匹配了这个值,那么模式之后的代码将被执行。如果模式并不匹配这个值,将继续执行下一个分支。
|
|
|
|
|
|
|
|
|
|
|
|
每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 match 表达式的返回值。如果分支有多行代码,那么需要用`{}`包裹,同时最后一行代码需要是一个表达式。
|
|
|
|
每个分支相关联的代码是一个表达式,而表达式的结果值将作为整个 match 表达式的返回值。如果分支有多行代码,那么需要用`{}`包裹,同时最后一行代码需要是一个表达式。
|
|
|
|
|
|
|
|
|
|
|
@ -84,7 +99,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
#### 模式绑定
|
|
|
|
#### 模式绑定
|
|
|
|
|
|
|
|
|
|
|
|
匹配分支的另外一个重要功能是从模式中取出绑定的值,例如:
|
|
|
|
模式匹配的另外一个重要功能是从模式中取出绑定的值,例如:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
enum UsState {
|
|
|
|
enum UsState {
|
|
|
@ -100,7 +115,7 @@ enum Coin {
|
|
|
|
Quarter(UsState), // 25美分硬币
|
|
|
|
Quarter(UsState), // 25美分硬币
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
其中Coin::Quarter成员还存放了一个值:美国的某个州,因为在1999年到2008年间,美国在25美分(Quater)硬币的背后为50个州印刷了不同的设计。其它硬币都没有相关的设计。
|
|
|
|
其中Coin::Quarter成员还存放了一个值:美国的某个州,因为在1999年到2008年间,美国在25美分(Quater)硬币的背后为50个州印刷了不同的设计, 其它硬币都没有相关的设计。
|
|
|
|
|
|
|
|
|
|
|
|
接下来,我们希望在模式匹配中,获取到25美分硬币上刻印的州的名称:
|
|
|
|
接下来,我们希望在模式匹配中,获取到25美分硬币上刻印的州的名称:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
@ -116,7 +131,7 @@ fn value_in_cents(coin: Coin) -> u8 {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
上面代码中,在匹配`Coin::Quarter`模式时,我们把它内部存储的值绑定到了`state`变量上,因此`state`变量就是对应的`UsState`枚举类型。
|
|
|
|
上面代码中,在匹配`Coin::Quarter(state)`模式时,我们把它内部存储的值绑定到了`state`变量上,因此`state`变量就是对应的`UsState`枚举类型。
|
|
|
|
|
|
|
|
|
|
|
|
例如有一个印了阿拉斯加州标记的25分硬币:`Coin::Quarter(UsState::Alaska))`, 它在匹配时,`state`变量将被绑定`UsState::Alaska`的枚举值。
|
|
|
|
例如有一个印了阿拉斯加州标记的25分硬币:`Coin::Quarter(UsState::Alaska))`, 它在匹配时,`state`变量将被绑定`UsState::Alaska`的枚举值。
|
|
|
|
|
|
|
|
|
|
|
@ -185,7 +200,7 @@ fn main() {
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
我们没有处理`Direction::West`的情况,因此会报错:
|
|
|
|
我们没有处理`Direction::West`的情况,因此会报错:
|
|
|
|
```rust
|
|
|
|
```console
|
|
|
|
error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`West`没有被覆盖
|
|
|
|
error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`West`没有被覆盖
|
|
|
|
--> src/main.rs:10:11
|
|
|
|
--> src/main.rs:10:11
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -211,7 +226,7 @@ error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`
|
|
|
|
|
|
|
|
|
|
|
|
#### `_` 通配符
|
|
|
|
#### `_` 通配符
|
|
|
|
|
|
|
|
|
|
|
|
Rust 也提供了一个**模式**用于不想列举出所有可能值的场景。例如,`u8` 可以拥有 0 到 255 的有效的值,如果我们只关心 1、3、5 和 7 这几个值,就并不想列出其它的 0、2、4、6、8、9 一直到 255 的值。所幸, 我们不必这么做, 因为可以使用使用特殊的模式 `_` 替代:
|
|
|
|
Rust 也提供了一个**模式**用于不想列出所有可能值的场景。例如,`u8` 可以拥有 0 到 255 的有效的值,如果我们只关心 `1、3、5 和 7` 这几个值,就并不想列出其它的 `0、2、4、6、8、9 一直到 255` 的值。所幸, 我们不必一个一个列出所有值, 因为可以使用使用特殊的模式 `_` 替代:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
let some_u8_value = 0u8;
|
|
|
|
let some_u8_value = 0u8;
|
|
|
@ -226,10 +241,10 @@ match some_u8_value {
|
|
|
|
|
|
|
|
|
|
|
|
通过将其放置于其他分支后,`_`将会匹配所有遗漏的值。`()`表示啥都不做的意思,所以当匹配到`_`后,什么也不会发生。
|
|
|
|
通过将其放置于其他分支后,`_`将会匹配所有遗漏的值。`()`表示啥都不做的意思,所以当匹配到`_`后,什么也不会发生。
|
|
|
|
|
|
|
|
|
|
|
|
然后,在某些场景下,我们其实只关心**某一个值是否存在**,此时`match`就显得过于啰嗦,还好,Rust提供了`if let`.
|
|
|
|
然后,在某些场景下,我们其实只关心**某一个值是否存在**,此时`match`就显得过于啰嗦。
|
|
|
|
|
|
|
|
|
|
|
|
## `if let`匹配
|
|
|
|
## `if let`匹配
|
|
|
|
很多时候都会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,用`match`处理如下:
|
|
|
|
有时会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,用`match`处理如下:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
let v = Some(3u8);
|
|
|
|
let v = Some(3u8);
|
|
|
|
match v{
|
|
|
|
match v{
|
|
|
@ -238,7 +253,7 @@ match some_u8_value {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
````
|
|
|
|
````
|
|
|
|
|
|
|
|
|
|
|
|
我们想要对 `Some(3)` 模式进行匹配, 同时不想处理任何其他 `Some<u8>` 值或 `None` 值。但是为了满足`match`表达式(穷尽性)的要求,必须在处理完这唯一的成员后加上 `_ => ()`,这样也要增加很多样板代码。
|
|
|
|
我们想要对 `Some(3)` 模式进行匹配, 同时不想处理任何其他 `Some<u8>` 值或 `None` 值。但是为了满足`match`表达式(穷尽性)的要求,必须在处理完这唯一的成员后加上 `_ => ()`,这样会增加不少无用的代码。
|
|
|
|
|
|
|
|
|
|
|
|
杀鸡焉用牛刀,可以用`if let`的方式来实现:
|
|
|
|
杀鸡焉用牛刀,可以用`if let`的方式来实现:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
@ -285,3 +300,5 @@ fn main() {
|
|
|
|
println!("在匹配后,age是{:?}",age);
|
|
|
|
println!("在匹配后,age是{:?}",age);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
需要注意的是,**`match`中的变量覆盖其实不是那么的容易看出**,因此要小心!
|