|
|
@ -19,7 +19,7 @@ match x {
|
|
|
|
|
|
|
|
|
|
|
|
### 匹配命名变量
|
|
|
|
### 匹配命名变量
|
|
|
|
|
|
|
|
|
|
|
|
在[match一章](./match-if-let#变量覆盖)中,我们有讲过变量覆盖的问题,这个在**匹配命名变量**时会遇到:
|
|
|
|
在 [match 一章](./match-if-let#变量覆盖)中,我们有讲过变量覆盖的问题,这个在**匹配命名变量**时会遇到:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -38,9 +38,9 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
让我们看看当 `match` 语句运行的时候发生了什么。第一个匹配分支的模式并不匹配 `x` 中定义的值,所以代码继续执行。
|
|
|
|
让我们看看当 `match` 语句运行的时候发生了什么。第一个匹配分支的模式并不匹配 `x` 中定义的值,所以代码继续执行。
|
|
|
|
|
|
|
|
|
|
|
|
第二个匹配分支中的模式引入了一个新变量 `y`,它会匹配任何 `Some` 中的值。因为这里的`y`在 `match` 表达式的作用域中,而不是之前`main`作用域中,所以这是一个新变量,不是开头声明为值 10 的那个 `y`。这个新的 `y` 绑定会匹配任何 `Some` 中的值,在这里是 `x` 中的值。因此这个 `y` 绑定了 `x` 中 `Some` 内部的值。这个值是 5,所以这个分支的表达式将会执行并打印出 `Matched,y = 5`。
|
|
|
|
第二个匹配分支中的模式引入了一个新变量 `y`,它会匹配任何 `Some` 中的值。因为这里的 `y` 在 `match` 表达式的作用域中,而不是之前 `main` 作用域中,所以这是一个新变量,不是开头声明为值 10 的那个 `y`。这个新的 `y` 绑定会匹配任何 `Some` 中的值,在这里是 `x` 中的值。因此这个 `y` 绑定了 `x` 中 `Some` 内部的值。这个值是 5,所以这个分支的表达式将会执行并打印出 `Matched,y = 5`。
|
|
|
|
|
|
|
|
|
|
|
|
如果 `x` 的值是 `None` 而不是 `Some(5)`,头两个分支的模式不会匹配,所以会匹配模式`_`。这个分支的模式中没有引入变量 `x`,所以此时表达式中的 `x` 会是外部没有被覆盖的 `x`,也就是`Some(5)`。
|
|
|
|
如果 `x` 的值是 `None` 而不是 `Some(5)`,头两个分支的模式不会匹配,所以会匹配模式 `_`。这个分支的模式中没有引入变量 `x`,所以此时表达式中的 `x` 会是外部没有被覆盖的 `x`,也就是 `Some(5)`。
|
|
|
|
|
|
|
|
|
|
|
|
一旦 `match` 表达式执行完毕,其作用域也就结束了,同理内部 `y` 的作用域也结束了。最后的 `println!` 会打印 `at the end: x = Some(5), y = 10`。
|
|
|
|
一旦 `match` 表达式执行完毕,其作用域也就结束了,同理内部 `y` 的作用域也结束了。最后的 `println!` 会打印 `at the end: x = Some(5), y = 10`。
|
|
|
|
|
|
|
|
|
|
|
@ -62,7 +62,7 @@ match x {
|
|
|
|
|
|
|
|
|
|
|
|
上面的代码会打印 `one or two`。
|
|
|
|
上面的代码会打印 `one or two`。
|
|
|
|
|
|
|
|
|
|
|
|
### 通过序列`..=` 匹配值的范围
|
|
|
|
### 通过序列 `..=` 匹配值的范围
|
|
|
|
|
|
|
|
|
|
|
|
在[数值类型](../base-type/numbers#序列(Range))中我们有讲到一个序列语法,该语言不仅可以用循环中,还能用于匹配模式。
|
|
|
|
在[数值类型](../base-type/numbers#序列(Range))中我们有讲到一个序列语法,该语言不仅可以用循环中,还能用于匹配模式。
|
|
|
|
|
|
|
|
|
|
|
@ -77,7 +77,7 @@ match x {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
如果 `x` 是 1、2、3、4 或 5,第一个分支就会匹配。这相比使用 `|` 运算符表达相同的意思更为方便;相比 `1..=5`,使用 `|` 则不得不指定 `1 | 2 | 3 | 4 | 5`这五个值,而使用`..=`指定序列就简短的多,比如希望匹配比如从 1 到 1000 的数字的时候!
|
|
|
|
如果 `x` 是 1、2、3、4 或 5,第一个分支就会匹配。这相比使用 `|` 运算符表达相同的意思更为方便;相比 `1..=5`,使用 `|` 则不得不指定 `1 | 2 | 3 | 4 | 5` 这五个值,而使用 `..=` 指定序列就简短的多,比如希望匹配比如从 1 到 1000 的数字的时候!
|
|
|
|
|
|
|
|
|
|
|
|
序列只允许用于数字或字符类型,原因是:它们可以连续,同时编译器在编译期可以检查该序列是否为空,字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。
|
|
|
|
序列只允许用于数字或字符类型,原因是:它们可以连续,同时编译器在编译期可以检查该序列是否为空,字符和数字值是 Rust 中仅有的可以用于判断是否为空的类型。
|
|
|
|
|
|
|
|
|
|
|
@ -93,7 +93,7 @@ match x {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Rust 知道 `c` 位于第一个模式的序列内,所以会打印出 `early ASCII letter`。
|
|
|
|
Rust 知道 `'c'` 位于第一个模式的序列内,所以会打印出 `early ASCII letter`。
|
|
|
|
|
|
|
|
|
|
|
|
### 解构并分解值
|
|
|
|
### 解构并分解值
|
|
|
|
|
|
|
|
|
|
|
@ -101,7 +101,7 @@ Rust 知道 `c` 位于第一个模式的序列内,所以会打印出 `early AS
|
|
|
|
|
|
|
|
|
|
|
|
#### 解构结构体
|
|
|
|
#### 解构结构体
|
|
|
|
|
|
|
|
|
|
|
|
下面代码展示了如何用`let`解构一个带有两个字段 `x` 和 `y` 的结构体 `Point`:
|
|
|
|
下面代码展示了如何用 `let` 解构一个带有两个字段 `x` 和 `y` 的结构体 `Point`:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
struct Point {
|
|
|
|
struct Point {
|
|
|
@ -120,7 +120,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
这段代码创建了变量 `a` 和 `b` 来匹配结构体 `p` 中的 `x` 和 `y` 字段,这个例子展示了**模式中的变量名不必与结构体中的字段名一致**。不过通常希望变量名与字段名一致以便于理解变量来自于哪些字段。
|
|
|
|
这段代码创建了变量 `a` 和 `b` 来匹配结构体 `p` 中的 `x` 和 `y` 字段,这个例子展示了**模式中的变量名不必与结构体中的字段名一致**。不过通常希望变量名与字段名一致以便于理解变量来自于哪些字段。
|
|
|
|
|
|
|
|
|
|
|
|
因为变量名匹配字段名是常见的,同时因为 `let Point { x: x, y: y } = p;` 中`x` 和 `y`重复了,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称。下例与上例有着相同行为的代码,不过 `let` 模式创建的变量为 `x` 和 `y` 而不是 `a` 和 `b`:
|
|
|
|
因为变量名匹配字段名是常见的,同时因为 `let Point { x: x, y: y } = p;` 中 `x` 和 `y` 重复了,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称。下例与上例有着相同行为的代码,不过 `let` 模式创建的变量为 `x` 和 `y` 而不是 `a` 和 `b`:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
struct Point {
|
|
|
|
struct Point {
|
|
|
@ -138,7 +138,7 @@ fn main() {
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
这段代码创建了变量 `x` 和 `y`,与结构体`p` 中的 `x` 和 `y`字段相匹配。其结果是变量 `x` 和 `y` 包含结构体 `p` 中的值。
|
|
|
|
这段代码创建了变量 `x` 和 `y`,与结构体 `p` 中的 `x` 和 `y` 字段相匹配。其结果是变量 `x` 和 `y` 包含结构体 `p` 中的值。
|
|
|
|
|
|
|
|
|
|
|
|
也可以使用字面值作为结构体模式的一部分进行进行解构,而不是为所有的字段创建变量。这允许我们测试一些字段为特定值的同时创建其他字段的变量。
|
|
|
|
也可以使用字面值作为结构体模式的一部分进行进行解构,而不是为所有的字段创建变量。这允许我们测试一些字段为特定值的同时创建其他字段的变量。
|
|
|
|
|
|
|
|
|
|
|
@ -160,15 +160,15 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
首先是`match`第一个分支,指定匹配`y`为`0`的`Point`;
|
|
|
|
首先是 `match` 第一个分支,指定匹配 `y` 为 `0` 的 `Point`;
|
|
|
|
然后第二个分支在第一个分支之后,匹配`y`不为`0`,`x`为`0`的`Point`;
|
|
|
|
然后第二个分支在第一个分支之后,匹配 `y` 不为`0`,`x`为 `0` 的 `Point`;
|
|
|
|
最后一个分支匹配`x`不为`0`,`y`也不为`0`的`Point`。
|
|
|
|
最后一个分支匹配 `x` 不为 `0`,`y` 也不为 `0` 的 `Point`。
|
|
|
|
|
|
|
|
|
|
|
|
在这个例子中,值 `p` 因为其 `x` 包含 0 而匹配第二个分支,因此会打印出 `On the y axis at 7`。
|
|
|
|
在这个例子中,值 `p` 因为其 `x` 包含 0 而匹配第二个分支,因此会打印出 `On the y axis at 7`。
|
|
|
|
|
|
|
|
|
|
|
|
#### 解构枚举
|
|
|
|
#### 解构枚举
|
|
|
|
|
|
|
|
|
|
|
|
下面代码以`Message`枚举为例,编写一个 `match` 使用模式解构每一个内部值:
|
|
|
|
下面代码以 `Message` 枚举为例,编写一个 `match` 使用模式解构每一个内部值:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
enum Message {
|
|
|
|
enum Message {
|
|
|
@ -205,7 +205,7 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这里老生常谈一句话,模式匹配一样要类型相同,因此匹配`Message::Move{1,2}`这样的枚举值,就必须要用`Message::Move{x,y}`这样的同类型模式才行。
|
|
|
|
这里老生常谈一句话,模式匹配一样要类型相同,因此匹配 `Message::Move{1,2}` 这样的枚举值,就必须要用 `Message::Move{x,y}` 这样的同类型模式才行。
|
|
|
|
|
|
|
|
|
|
|
|
这段代码会打印出 `Change the color to red 0, green 160, and blue 255`。尝试改变 `msg` 的值来观察其他分支代码的运行。
|
|
|
|
这段代码会打印出 `Change the color to red 0, green 160, and blue 255`。尝试改变 `msg` 的值来观察其他分支代码的运行。
|
|
|
|
|
|
|
|
|
|
|
@ -215,7 +215,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
#### 解构嵌套的结构体和枚举
|
|
|
|
#### 解构嵌套的结构体和枚举
|
|
|
|
|
|
|
|
|
|
|
|
目前为止,所有的例子都只匹配了深度为一级的结构体或枚举。 `match`也可以匹配嵌套的项!
|
|
|
|
目前为止,所有的例子都只匹配了深度为一级的结构体或枚举。 `match` 也可以匹配嵌套的项!
|
|
|
|
|
|
|
|
|
|
|
|
例如使用下面的代码来同时支持 RGB 和 HSV 色彩模式:
|
|
|
|
例如使用下面的代码来同时支持 RGB 和 HSV 色彩模式:
|
|
|
|
|
|
|
|
|
|
|
@ -256,7 +256,7 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
`match`第一个分支的模式匹配一个`Message::ChangeColor`枚举成员,该枚举成员又包含了一个`Color::Rgb`的枚举成员,最终绑定了3个内部的`i32`值。第二个,就交给亲爱的读者来思考完成。
|
|
|
|
`match` 第一个分支的模式匹配一个 `Message::ChangeColor` 枚举成员,该枚举成员又包含了一个 `Color::Rgb` 的枚举成员,最终绑定了 3 个内部的 `i32` 值。第二个,就交给亲爱的读者来思考完成。
|
|
|
|
|
|
|
|
|
|
|
|
#### 解构结构体和元组
|
|
|
|
#### 解构结构体和元组
|
|
|
|
|
|
|
|
|
|
|
@ -275,7 +275,7 @@ let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 });
|
|
|
|
|
|
|
|
|
|
|
|
### 忽略模式中的值
|
|
|
|
### 忽略模式中的值
|
|
|
|
|
|
|
|
|
|
|
|
有时忽略模式中的一些值是很有用的,比如在`match`中的最后一个分支使用`_`模式匹配所有剩余的值。 你也可以在另一个模式中使用 `_` 模式,使用一个以下划线开始的名称,或者使用 `..` 忽略所剩部分的值。
|
|
|
|
有时忽略模式中的一些值是很有用的,比如在 `match` 中的最后一个分支使用 `_` 模式匹配所有剩余的值。 你也可以在另一个模式中使用 `_` 模式,使用一个以下划线开始的名称,或者使用 `..` 忽略所剩部分的值。
|
|
|
|
|
|
|
|
|
|
|
|
#### 使用 `_` 忽略整个值
|
|
|
|
#### 使用 `_` 忽略整个值
|
|
|
|
|
|
|
|
|
|
|
@ -298,7 +298,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
#### 使用嵌套的 `_` 忽略部分值
|
|
|
|
#### 使用嵌套的 `_` 忽略部分值
|
|
|
|
|
|
|
|
|
|
|
|
可以在一个模式内部使用`_` 忽略部分值:
|
|
|
|
可以在一个模式内部使用 `_` 忽略部分值:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
let mut setting_value = Some(5);
|
|
|
|
let mut setting_value = Some(5);
|
|
|
@ -319,8 +319,8 @@ println!("setting is {:?}", setting_value);
|
|
|
|
|
|
|
|
|
|
|
|
这段代码会打印出 `Can't overwrite an existing customized value` 接着是 `setting is Some(5)`。
|
|
|
|
这段代码会打印出 `Can't overwrite an existing customized value` 接着是 `setting is Some(5)`。
|
|
|
|
|
|
|
|
|
|
|
|
第一个匹配分支,我们不关心里面的值,只关心元组中两个元素的类型,因此对于`Some`中的值,直接进行忽略。
|
|
|
|
第一个匹配分支,我们不关心里面的值,只关心元组中两个元素的类型,因此对于 `Some` 中的值,直接进行忽略。
|
|
|
|
剩下的形如`(Some(_),None)`,`(None, Some(_))`, `(None,None)`形式,都由第二个分支`_`进行分配。
|
|
|
|
剩下的形如 `(Some(_),None)`,`(None, Some(_))`, `(None,None)` 形式,都由第二个分支 `_` 进行分配。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
还可以在一个模式中的多处使用下划线来忽略特定值,如下所示,这里忽略了一个五元元组中的第二和第四个值:
|
|
|
|
还可以在一个模式中的多处使用下划线来忽略特定值,如下所示,这里忽略了一个五元元组中的第二和第四个值:
|
|
|
@ -335,13 +335,13 @@ match numbers {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
老生常谈:模式匹配一定要类型相同,因此匹配`numbers`元组的模式,也必须有五个值(元组中元素的数量也属于元组类型的一部分)。
|
|
|
|
老生常谈:模式匹配一定要类型相同,因此匹配 `numbers` 元组的模式,也必须有五个值(元组中元素的数量也属于元组类型的一部分)。
|
|
|
|
|
|
|
|
|
|
|
|
这会打印出 `Some numbers: 2, 8, 32`, 值 4 和 16 会被忽略。
|
|
|
|
这会打印出 `Some numbers: 2, 8, 32`, 值 4 和 16 会被忽略。
|
|
|
|
|
|
|
|
|
|
|
|
#### 使用下划线开头忽略未使用的变量
|
|
|
|
#### 使用下划线开头忽略未使用的变量
|
|
|
|
|
|
|
|
|
|
|
|
如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个bug。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头:
|
|
|
|
如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个 BUG。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -350,7 +350,7 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这里得到了警告说未使用变量 `y`,至于`x`则没有警告。
|
|
|
|
这里得到了警告说未使用变量 `y`,至于 `x` 则没有警告。
|
|
|
|
|
|
|
|
|
|
|
|
注意, 只使用 `_` 和使用以下划线开头的名称有些微妙的不同:比如 **`_x` 仍会将值绑定到变量,而 `_` 则完全不会绑定**。
|
|
|
|
注意, 只使用 `_` 和使用以下划线开头的名称有些微妙的不同:比如 **`_x` 仍会将值绑定到变量,而 `_` 则完全不会绑定**。
|
|
|
|
|
|
|
|
|
|
|
@ -363,7 +363,7 @@ if let Some(_s) = s {
|
|
|
|
|
|
|
|
|
|
|
|
println!("{:?}", s);
|
|
|
|
println!("{:?}", s);
|
|
|
|
```
|
|
|
|
```
|
|
|
|
`s`是一个拥有所有权的动态字符串,在上面代码中,我们会得到一个错误,因为 `s` 的值会被转移给 `_s`,在`println!`中再次使用`s`会报错:
|
|
|
|
`s` 是一个拥有所有权的动态字符串,在上面代码中,我们会得到一个错误,因为 `s` 的值会被转移给 `_s`,在 `println!` 中再次使用 `s` 会报错:
|
|
|
|
```console
|
|
|
|
```console
|
|
|
|
error[E0382]: borrow of partially moved value: `s`
|
|
|
|
error[E0382]: borrow of partially moved value: `s`
|
|
|
|
--> src/main.rs:8:22
|
|
|
|
--> src/main.rs:8:22
|
|
|
@ -389,7 +389,7 @@ println!("{:?}", s);
|
|
|
|
|
|
|
|
|
|
|
|
#### 用 `..` 忽略剩余值
|
|
|
|
#### 用 `..` 忽略剩余值
|
|
|
|
|
|
|
|
|
|
|
|
对于有多个部分的值,可以使用 `..` 语法来只使用部分值而忽略其它值,这样也不用再为每一个被忽略的值都单独列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分.
|
|
|
|
对于有多个部分的值,可以使用 `..` 语法来只使用部分值而忽略其它值,这样也不用再为每一个被忽略的值都单独列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分。
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
struct Point {
|
|
|
|
struct Point {
|
|
|
@ -405,9 +405,9 @@ match origin {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这里列出了 `x` 值,接着使用了`..` 模式来忽略其它字段,这样的写法要比一一列出其它字段,然后用`_`忽略简洁的多。
|
|
|
|
这里列出了 `x` 值,接着使用了 `..` 模式来忽略其它字段,这样的写法要比一一列出其它字段,然后用 `_` 忽略简洁的多。
|
|
|
|
|
|
|
|
|
|
|
|
还可以用`..`来忽略元组中间的某些值:
|
|
|
|
还可以用 `..` 来忽略元组中间的某些值:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -440,7 +440,7 @@ fn main() {
|
|
|
|
如果编译上面的例子,会得到下面的错误:
|
|
|
|
如果编译上面的例子,会得到下面的错误:
|
|
|
|
|
|
|
|
|
|
|
|
```text
|
|
|
|
```text
|
|
|
|
error: `..` can only be used once per tuple pattern // 每个元组模式只能使用一个`..`
|
|
|
|
error: `..` can only be used once per tuple pattern // 每个元组模式只能使用一个 `..`
|
|
|
|
--> src/main.rs:5:22
|
|
|
|
--> src/main.rs:5:22
|
|
|
|
|
|
|
|
|
|
|
|
|
|
5 | (.., second, ..) => {
|
|
|
|
5 | (.., second, ..) => {
|
|
|
@ -451,7 +451,7 @@ error: `..` can only be used once per tuple pattern // 每个元组模式只能
|
|
|
|
error: could not compile `world_hello` due to previous error ^^
|
|
|
|
error: could not compile `world_hello` due to previous error ^^
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Rust无法判断,`second`应该匹配`numbers`中的第几个元素,因此这里使用两个`..`模式,是由很大歧义的!
|
|
|
|
Rust无法判断,`second` 应该匹配 `numbers` 中的第几个元素,因此这里使用两个 `..` 模式,是由很大歧义的!
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 匹配守卫提供的额外条件
|
|
|
|
### 匹配守卫提供的额外条件
|
|
|
@ -470,11 +470,11 @@ match num {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
这个例子会打印出 `less than five: 4`。当 `num` 与模式中第一个分支匹配时,`Some(4)` 可以与 `Some(x)`匹配,接着匹配守卫检查 `x` 值是否小于 `5`,因为 `4` 小于 `5`,所以第一个分支被选择。
|
|
|
|
这个例子会打印出 `less than five: 4`。当 `num` 与模式中第一个分支匹配时,`Some(4)` 可以与 `Some(x)` 匹配,接着匹配守卫检查 `x` 值是否小于 5,因为 4 小于 5,所以第一个分支被选择。
|
|
|
|
|
|
|
|
|
|
|
|
相反如果 `num` 为 `Some(10)`,因为 10 不小于 5 ,所以第一个分支的匹配守卫为假。接着 Rust 会前往第二个分支,因为这里没有匹配守卫所以会匹配任何 `Some` 成员。
|
|
|
|
相反如果 `num` 为 `Some(10)`,因为 10 不小于 5 ,所以第一个分支的匹配守卫为假。接着 Rust 会前往第二个分支,因为这里没有匹配守卫所以会匹配任何 `Some` 成员。
|
|
|
|
|
|
|
|
|
|
|
|
模式中无法提供类如`if x < 5`的表达能力,我们可以通过匹配守卫的方式来实现。
|
|
|
|
模式中无法提供类如 `if x < 5` 的表达能力,我们可以通过匹配守卫的方式来实现。
|
|
|
|
|
|
|
|
|
|
|
|
在之前,我们提到可以使用匹配守卫来解决模式中变量覆盖的问题,那里 `match` 表达式的模式中新建了一个变量而不是使用 `match` 之外的同名变量。内部变量覆盖了外部变量,意味着此时不能够使用外部变量的值,下面代码展示了如何使用匹配守卫修复这个问题。
|
|
|
|
在之前,我们提到可以使用匹配守卫来解决模式中变量覆盖的问题,那里 `match` 表达式的模式中新建了一个变量而不是使用 `match` 之外的同名变量。内部变量覆盖了外部变量,意味着此时不能够使用外部变量的值,下面代码展示了如何使用匹配守卫修复这个问题。
|
|
|
|
|
|
|
|
|
|
|
@ -497,7 +497,7 @@ fn main() {
|
|
|
|
|
|
|
|
|
|
|
|
匹配守卫 `if n == y` 并不是一个模式所以没有引入新变量。这个 `y` **正是** 外部的 `y` 而不是新的覆盖变量 `y`,这样就可以通过比较 `n` 和 `y` 来表达寻找一个与外部 `y` 相同的值的概念了。
|
|
|
|
匹配守卫 `if n == y` 并不是一个模式所以没有引入新变量。这个 `y` **正是** 外部的 `y` 而不是新的覆盖变量 `y`,这样就可以通过比较 `n` 和 `y` 来表达寻找一个与外部 `y` 相同的值的概念了。
|
|
|
|
|
|
|
|
|
|
|
|
也可以在匹配守卫中使用 **或** 运算符 `|` 来指定多个模式,**同时匹配守卫的条件会作用于所有的模式**。下面代码展示了匹配守卫与 `|` 的优先级。这个例子中看起来好像 `if y` 只作用于 `6`,但实际上匹配守卫 `if y` 作用于 `4`、`5` **和** `6` ,在满足`x`属于 `4 | 5 | 6` 后才会判断 `y` 是否为 `true`:
|
|
|
|
也可以在匹配守卫中使用 **或** 运算符 `|` 来指定多个模式,**同时匹配守卫的条件会作用于所有的模式**。下面代码展示了匹配守卫与 `|` 的优先级。这个例子中看起来好像 `if y` 只作用于 `6`,但实际上匹配守卫 `if y` 作用于 `4`、`5` **和** `6` ,在满足 `x` 属于 `4 | 5 | 6` 后才会判断 `y` 是否为 `true`:
|
|
|
|
|
|
|
|
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
let x = 4;
|
|
|
|
let x = 4;
|
|
|
@ -553,16 +553,16 @@ match msg {
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
上例会打印出 `Found an id in range: 5`。通过在 `3..=7` 之前指定 `id_variable @`,我们捕获了任何匹配此范围的值并同时将该值绑定到变量`id_variable`上。
|
|
|
|
上例会打印出 `Found an id in range: 5`。通过在 `3..=7` 之前指定 `id_variable @`,我们捕获了任何匹配此范围的值并同时将该值绑定到变量 `id_variable` 上。
|
|
|
|
|
|
|
|
|
|
|
|
第二个分支只在模式中指定了一个范围,`id` 字段的值可以是 `10、11 或 12`,不过这个模式的代码并不知情也不能使用 `id` 字段中的值,因为没有将 `id` 值保存进一个变量。
|
|
|
|
第二个分支只在模式中指定了一个范围,`id` 字段的值可以是 `10、11 或 12`,不过这个模式的代码并不知情也不能使用 `id` 字段中的值,因为没有将 `id` 值保存进一个变量。
|
|
|
|
|
|
|
|
|
|
|
|
最后一个分支指定了一个没有范围的变量,此时确实拥有可以用于分支代码的变量 `id`,因为这里使用了结构体字段简写语法。不过此分支中没有像头两个分支那样对 `id` 字段的值进行测试:任何值都会匹配此分支。
|
|
|
|
最后一个分支指定了一个没有范围的变量,此时确实拥有可以用于分支代码的变量 `id`,因为这里使用了结构体字段简写语法。不过此分支中没有像头两个分支那样对 `id` 字段的值进行测试:任何值都会匹配此分支。
|
|
|
|
|
|
|
|
|
|
|
|
当你既想要限定分支范围,又想要使用分支的变量时,就可以用`@`来绑定到一个新的变量上,实现想要的功能。
|
|
|
|
当你既想要限定分支范围,又想要使用分支的变量时,就可以用 `@` 来绑定到一个新的变量上,实现想要的功能。
|
|
|
|
|
|
|
|
|
|
|
|
#### @前绑定后解构(Rust1.56新增)
|
|
|
|
#### @前绑定后解构(Rust 1.56 新增)
|
|
|
|
使用`@`还可以在绑定新变量的同时,对目标进行解构:
|
|
|
|
使用 `@` 还可以在绑定新变量的同时,对目标进行解构:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[derive(Debug)]
|
|
|
|
struct Point {
|
|
|
|
struct Point {
|
|
|
@ -571,7 +571,7 @@ struct Point {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
|
// 绑定新变量`p`,同时对`Point`进行解构
|
|
|
|
// 绑定新变量 `p`,同时对 `Point` 进行解构
|
|
|
|
let p @ Point {x: px, y: py } = Point {x: 10, y: 23};
|
|
|
|
let p @ Point {x: px, y: py } = Point {x: 10, y: 23};
|
|
|
|
println!("x: {}, y: {}", px, py);
|
|
|
|
println!("x: {}, y: {}", px, py);
|
|
|
|
println!("{:?}", p);
|
|
|
|
println!("{:?}", p);
|
|
|
@ -586,7 +586,7 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
#### @新特性(Rust1.53新增)
|
|
|
|
#### @新特性(Rust 1.53 新增)
|
|
|
|
考虑下面一段代码:
|
|
|
|
考虑下面一段代码:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
fn main() {
|
|
|
|
fn main() {
|
|
|
@ -598,12 +598,12 @@ fn main() {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
```
|
|
|
|
编译不通过,是因为`num`没有绑定到所有的模式上,只绑定了模式`1`,你可能会试图通过这个方式来解决:
|
|
|
|
编译不通过,是因为 `num` 没有绑定到所有的模式上,只绑定了模式 `1`,你可能会试图通过这个方式来解决:
|
|
|
|
```rust
|
|
|
|
```rust
|
|
|
|
num @ (1 | 2)
|
|
|
|
num @ (1 | 2)
|
|
|
|
```
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
但是,如果你用的是Rust1.53之前的版本,那这种写法会报错,因为编译器不支持。
|
|
|
|
但是,如果你用的是 Rust 1.53 之前的版本,那这种写法会报错,因为编译器不支持。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
至此,模式匹配的内容已经全部完结,复杂但是详尽,想要一次性全部记住属实不易,因此读者可以先留一个印象,等未来需要时,再来翻阅寻找具体的模式实现方式。
|
|
|
|
至此,模式匹配的内容已经全部完结,复杂但是详尽,想要一次性全部记住属实不易,因此读者可以先留一个印象,等未来需要时,再来翻阅寻找具体的模式实现方式。
|
|
|
|