|
|
@ -2,7 +2,7 @@
|
|
|
|
|
|
|
|
|
|
|
|
在Rust中,模式匹配最常用的就是`match`和`if let`,本章节将对两者及相关的概念进行详尽介绍。
|
|
|
|
在Rust中,模式匹配最常用的就是`match`和`if let`,本章节将对两者及相关的概念进行详尽介绍。
|
|
|
|
|
|
|
|
|
|
|
|
先来看一个关于`match`的简单例子:
|
|
|
|
先来看一个关于`match`的简单例子:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
enum Direction {
|
|
|
|
enum Direction {
|
|
|
|
East,
|
|
|
|
East,
|
|
|
@ -24,12 +24,12 @@ fn main() {
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这里我们想去匹配`dire`对应的枚举类型,因此在match中用三个匹配分支来完全覆盖枚举变量`Direction`的所有成员类型,有以下几点值得注意:
|
|
|
|
这里我们想去匹配`dire`对应的枚举类型,因此在match中用三个匹配分支来完全覆盖枚举变量`Direction`的所有成员类型,有以下几点值得注意:
|
|
|
|
- `match`的匹配必须要穷举出所有可能,因此这里用`_`来代表其余的所有可能性
|
|
|
|
- `match`的匹配必须要穷举出所有可能,因此这里用`_`来代表未列出的所有可能性
|
|
|
|
- `match`的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同
|
|
|
|
- `match`的每一个分支都必须是一个表达式,且所有分支的表达式最终返回值的类型必须相同
|
|
|
|
- **X | Y**, 是逻辑运算符`或`,代表该分支可以匹配`X`也可以匹配`Y`,只要满足一个即可
|
|
|
|
- **X | Y**,是逻辑运算符`或`,代表该分支可以匹配`X`也可以匹配`Y`,只要满足一个即可
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
其实`match`跟其他语言中的`switch`非常像, 而且`_`类似`switch`中的`default`。
|
|
|
|
其实`match`跟其他语言中的`switch`非常像,`_`类似于`switch`中的`default`。
|
|
|
|
|
|
|
|
|
|
|
|
## `match`匹配
|
|
|
|
## `match`匹配
|
|
|
|
|
|
|
|
|
|
|
@ -73,9 +73,9 @@ 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 表达式的返回值。如果分支有多行代码,那么需要用`{}`包裹,同时最后一行代码需要是一个表达式。
|
|
|
|
|
|
|
|
|
|
|
@ -98,7 +98,7 @@ fn main() {
|
|
|
|
println!("{}", ip_str);
|
|
|
|
println!("{}", ip_str);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
这里因为匹配到`_`分支,因此将`"::1"`赋值给了`ip_str`.
|
|
|
|
因为这里匹配到`_`分支,所以将`"::1"`赋值给了`ip_str`.
|
|
|
|
|
|
|
|
|
|
|
|
#### 模式绑定
|
|
|
|
#### 模式绑定
|
|
|
|
|
|
|
|
|
|
|
@ -118,7 +118,7 @@ enum Coin {
|
|
|
|
Quarter(UsState), // 25美分硬币
|
|
|
|
Quarter(UsState), // 25美分硬币
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
其中`Coin::Quarter`成员还存放了一个值:美国的某个州,因为在1999年到2008年间,美国在25美分(Quarter)硬币的背后为50个州印刷了不同的设计, 其它硬币都没有相关的设计。
|
|
|
|
其中`Coin::Quarter`成员还存放了一个值:美国的某个州(因为在1999年到2008年间,美国在25美分(Quarter)硬币的背后为50个州印刷了不同的标记,其它硬币都没有这样的设计)。
|
|
|
|
|
|
|
|
|
|
|
|
接下来,我们希望在模式匹配中,获取到25美分硬币上刻印的州的名称:
|
|
|
|
接下来,我们希望在模式匹配中,获取到25美分硬币上刻印的州的名称:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
@ -182,7 +182,7 @@ change color into '(r:255, g:255, b:0)', 'b' has been ignored
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### 穷尽匹配
|
|
|
|
#### 穷尽匹配
|
|
|
|
在文章的开头,我们简单总结过`match`的匹配必须穷尽所有情况,下面来举例说明,例如:
|
|
|
|
在文章的开头,我们简单总结过`match`的匹配必须穷尽所有情况,下面来举例说明,例如:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
enum Direction {
|
|
|
|
enum Direction {
|
|
|
|
East,
|
|
|
|
East,
|
|
|
@ -225,11 +225,11 @@ error[E0004]: non-exhaustive patterns: `West` not covered // 非穷尽匹配,`
|
|
|
|
|
|
|
|
|
|
|
|
首先,不禁想感叹,`Rust`的编译器真**强大,忍不住爆粗口了,sorry,如果你以后进一步深入使用Rust也会像我这样感叹的。
|
|
|
|
首先,不禁想感叹,`Rust`的编译器真**强大,忍不住爆粗口了,sorry,如果你以后进一步深入使用Rust也会像我这样感叹的。
|
|
|
|
|
|
|
|
|
|
|
|
其次,Rust知道`match`中没有覆盖的具体分支,甚至知道哪些模式被遗忘了。这种设计初心是为了保证我们处理所有的情况,特别是那种会造成十亿美金的空值问题。
|
|
|
|
其次,Rust知道`match`中没有覆盖的具体分支,知道哪些模式没有被覆盖。这种设计的初衷是为了保证我们能处理所有的情况,特别是那种会造成十亿美金的空值问题(不太读得懂)。
|
|
|
|
|
|
|
|
|
|
|
|
#### `_` 通配符
|
|
|
|
#### `_` 通配符
|
|
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
@ -242,12 +242,12 @@ match some_u8_value {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
通过将其放置于其他分支后,`_`将会匹配所有遗漏的值。`()`表示啥都不做的意思,所以当匹配到`_`后,什么也不会发生。
|
|
|
|
通过将`_`其放置于其他分支后,`_`将会匹配所有遗漏的值。`()`表示啥都不做的意思,所以当匹配到`_`后,什么也不会发生。
|
|
|
|
|
|
|
|
|
|
|
|
然后,在某些场景下,我们其实只关心**某一个值是否存在**,此时`match`就显得过于啰嗦。
|
|
|
|
然后,在某些场景下,我们其实只关心**某一个值是否存在**,此时`match`就显得过于啰嗦。
|
|
|
|
|
|
|
|
|
|
|
|
## `if let`匹配
|
|
|
|
## `if let`匹配
|
|
|
|
有时会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,用`match`处理如下:
|
|
|
|
有时会遇到只有一个模式的值需要被处理,其它值直接忽略的场景,如果用`match`来处理就要写成下面这样:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
let v = Some(3u8);
|
|
|
|
let v = Some(3u8);
|
|
|
|
match v{
|
|
|
|
match v{
|
|
|
@ -256,7 +256,7 @@ match some_u8_value {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
````
|
|
|
|
````
|
|
|
|
|
|
|
|
|
|
|
|
我们想要对 `Some(3)` 模式进行匹配, 同时不想处理任何其他 `Some<u8>` 值或 `None` 值。但是为了满足`match`表达式(穷尽性)的要求,必须在处理完这唯一的成员后加上 `_ => ()`,这样会增加不少无用的代码。
|
|
|
|
我们只想要对 `Some(3)` 模式进行匹配, 不想处理任何其他 `Some<u8>` 值或 `None` 值。但是为了满足`match`表达式(穷尽性)的要求,写代码时必须在处理完这唯一的成员后加上 `_ => ()`,这样会增加不少无用的代码。
|
|
|
|
|
|
|
|
|
|
|
|
杀鸡焉用牛刀,可以用`if let`的方式来实现:
|
|
|
|
杀鸡焉用牛刀,可以用`if let`的方式来实现:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
@ -265,10 +265,10 @@ if let Some(3) = some_u8_value {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这两种匹配对于新手来说,可能有些难以抉择,但是只要记住一点就好: **当你只要匹配一个条件,且忽略其他条件时就用`if let`,否则都用match**.
|
|
|
|
这两种匹配对于新手来说,可能有些难以抉择,但是只要记住一点就好:**当你只要匹配一个条件,且忽略其他条件时就用`if let`,否则都用match**。
|
|
|
|
|
|
|
|
|
|
|
|
## matches!宏
|
|
|
|
## matches!宏
|
|
|
|
Rust标准库中提供了一个非常实用的宏:`matches!`,它可以将一个表达式跟模式进行匹配,然后返回匹配的结果`true` or `false`.
|
|
|
|
Rust标准库中提供了一个非常实用的宏:`matches!`,它可以将一个表达式跟模式进行匹配,然后返回匹配的结果`true` or `false`。
|
|
|
|
|
|
|
|
|
|
|
|
例如,有一个动态数组,里面存有以下枚举:
|
|
|
|
例如,有一个动态数组,里面存有以下枚举:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
@ -282,12 +282,12 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
现在如果想对`v`进行过滤,只保留类型是`MyEnum::Foo`的元素,你可能想这么写:
|
|
|
|
现在如果想对`v`进行过滤,只保留类型是`MyEnum::Foo`的元素,你可能想这么写:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
v.iter().filter(|x| x == MyEnum::Foo);
|
|
|
|
v.iter().filter(|x| x == MyEnum::Foo);
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
但是,实际上这行代码会报错,因为你无法将`x`跟一个类型进行比较。好在,你可以使用`match`来完成,但是会导致代码更为啰嗦,是否有更简洁的方式?答案是使用`matches!`:
|
|
|
|
但是,实际上这行代码会报错,因为`x`的类型是`&&MyEnum`,无法跟类型`MyEnum`通过`==`进行比较(这里不太懂想表达什么,编译器跑了一下报了这个错误)。好在,你可以使用`match`来完成,但是会导致代码更为啰嗦,是否有更简洁的方式?答案是使用`matches!`:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
v.iter().filter(|x| matches!(x, MyEnum::Foo));
|
|
|
|
v.iter().filter(|x| matches!(x, MyEnum::Foo));
|
|
|
|
```
|
|
|
|
```
|
|
|
|