mirror of
https://github.com/KaiserY/trpl-zh-cn
synced 2024-11-09 08:51:18 +08:00
update ch18
This commit is contained in:
parent
f4b384bb11
commit
7ef91fa9ae
@ -2,9 +2,9 @@
|
||||
|
||||
> [ch18-00-patterns.md](https://github.com/rust-lang/book/blob/main/src/ch18-00-patterns.md)
|
||||
> <br>
|
||||
> commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f
|
||||
> commit 6fce661a0938aa0da06526e7b8f98fd7e67a222f
|
||||
|
||||
模式是 Rust 中特殊的语法,它用来匹配类型中的结构,无论类型是简单还是复杂。结合使用模式和 `match` 表达式以及其他结构可以提供更多对程序控制流的支配权。模式由如下一些内容组合而成:
|
||||
**模式**(*Patterns*)是 Rust 中特殊的语法,它用来匹配类型中的结构,无论类型是简单还是复杂。结合使用模式和 `match` 表达式以及其他结构可以提供更多对程序控制流的支配权。模式由如下一些内容组合而成:
|
||||
|
||||
* 字面值
|
||||
* 解构的数组、枚举、结构体或者元组
|
||||
@ -12,7 +12,7 @@
|
||||
* 通配符
|
||||
* 占位符
|
||||
|
||||
这些部分描述了我们要处理的数据的形状,接着可以用其匹配值来决定程序是否拥有正确的数据来运行特定部分的代码。
|
||||
一些模式的例子包括`x`, `(a, 3)` 和 `Some(Color::Red)`。在模式为有效的上下文中,这些部分描述了数据的形状。接着可以用其匹配值来决定程序是否拥有正确的数据来运行特定部分的代码。
|
||||
|
||||
我们通过将一些值与模式相比较来使用它。如果模式匹配这些值,我们对值部分进行相应处理。回忆一下第六章讨论 `match` 表达式时像硬币分类器那样使用模式。如果数据符合这个形状,就可以使用这些命名的片段。如果不符合,与该模式相关的代码则不会运行。
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
> [ch18-01-all-the-places-for-patterns.md](https://github.com/rust-lang/book/blob/main/src/ch18-01-all-the-places-for-patterns.md)
|
||||
> <br>
|
||||
> commit 8a1aad812b90126974853f80d9217e07bd226650
|
||||
> commit 0c2d41e2afce734825c3a12087d423e8c2f0ae53
|
||||
|
||||
模式出现在 Rust 的很多地方。你已经在不经意间使用了很多模式!本部分是一个所有有效模式位置的参考。
|
||||
|
||||
@ -18,6 +18,17 @@ match VALUE {
|
||||
}
|
||||
```
|
||||
|
||||
例如这是一个来自示例 6-5 中匹配变量 `x` 中 `Option<i32>` 值的 `match` 表达式:
|
||||
|
||||
```rust,ignore
|
||||
match x {
|
||||
None => None,
|
||||
Some(i) => Some(i + 1),
|
||||
}
|
||||
```
|
||||
|
||||
这个 `match` 表达式中的模式为每个箭头左边的 `None` 和 `Some(i)`。
|
||||
|
||||
`match` 表达式必须是 **穷尽**(*exhaustive*)的,意为 `match` 表达式所有可能的值都必须被考虑到。一个确保覆盖每个可能值的方法是在最后一个分支使用捕获所有的模式:比如,一个匹配任何值的名称永远也不会失败,因此可以覆盖所有匹配剩下的情况。
|
||||
|
||||
有一个特定的模式 `_` 可以匹配所有情况,不过它从不绑定任何变量。这在例如希望忽略任何未指定值的情况很有用。本章之后的 [“忽略模式中的值”][ignoring-values-in-a-pattern] 部分会详细介绍 `_` 模式的更多细节。
|
||||
@ -26,7 +37,7 @@ match VALUE {
|
||||
|
||||
第六章讨论过了 `if let` 表达式,以及它是如何主要用于编写等同于只关心一个情况的 `match` 语句简写的。`if let` 可以对应一个可选的带有代码的 `else` 在 `if let` 中的模式不匹配时运行。
|
||||
|
||||
示例 18-1 展示了也可以组合并匹配 `if let`、`else if` 和 `else if let` 表达式。这相比 `match` 表达式一次只能将一个值与模式比较提供了更多灵活性;一系列 `if let`、`else if`、`else if let` 分支并不要求其条件相互关联。
|
||||
示例 18-1 展示了也可以组合并匹配 `if let`、`else if` 和 `else if let` 表达式。这相比 `match` 表达式一次只能将一个值与模式比较提供了更多灵活性。并且 Rust 并不要求一系列 `if let`、`else if`、`else if let` 分支的条件相互关联。
|
||||
|
||||
示例 18-1 中的代码展示了一系列针对不同条件的检查来决定背景颜色应该是什么。为了达到这个例子的目的,我们创建了硬编码值的变量,在真实程序中则可能由询问用户获得。
|
||||
|
||||
@ -38,7 +49,7 @@ match VALUE {
|
||||
|
||||
<span class="caption">示例 18-1: 结合 `if let`、`else if`、`else if let` 以及 `else`</span>
|
||||
|
||||
如果用户指定了中意的颜色,将使用其作为背景颜色。如果今天是星期二,背景颜色将是绿色。如果用户指定了他们的年龄字符串并能够成功将其解析为数字的话,我们将根据这个数字使用紫色或者橙色。最后,如果没有一个条件符合,背景颜色将是蓝色:
|
||||
如果用户指定了中意的颜色,将使用其作为背景颜色。如果没有指定中意的颜色且今天是星期二,背景颜色将是绿色。如果用户指定了他们的年龄字符串并能够成功将其解析为数字的话,我们将根据这个数字使用紫色或者橙色。最后,如果没有一个条件符合,背景颜色将是蓝色。
|
||||
|
||||
这个条件结构允许我们支持复杂的需求。使用这里硬编码的值,例子会打印出 `Using purple as the background color`。
|
||||
|
||||
@ -60,9 +71,7 @@ match VALUE {
|
||||
|
||||
### `for` 循环
|
||||
|
||||
如同第三章所讲的,`for` 循环是 Rust 中最常见的循环结构,不过还没有讲到的是 `for` 可以获取一个模式。在 `for` 循环中,模式是 `for` 关键字直接跟随的值,正如 `for x in y` 中的 `x`。
|
||||
|
||||
示例 18-3 中展示了如何使用 `for` 循环来解构,或拆开一个元组作为 `for` 循环的一部分:
|
||||
在 `for` 循环中,模式是 `for` 关键字直接跟随的值,正如 `for x in y` 中的 `x`。示例 18-3 中展示了如何使用 `for` 循环来解构,或拆开一个元组作为 `for` 循环的一部分:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-03/src/main.rs:here}}
|
||||
@ -86,7 +95,7 @@ match VALUE {
|
||||
let x = 5;
|
||||
```
|
||||
|
||||
本书进行了不下百次这样的操作,不过你可能没有发觉,这正是在使用模式!`let` 语句更为正式的样子如下:
|
||||
不过你可能没有发觉,每一次像这样使用 `let` 语句就是在使用模式!`let` 语句更为正式的样子如下:
|
||||
|
||||
```text
|
||||
let PATTERN = EXPRESSION;
|
||||
@ -118,7 +127,7 @@ let PATTERN = EXPRESSION;
|
||||
{{#include ../listings/ch18-patterns-and-matching/listing-18-05/output.txt}}
|
||||
```
|
||||
|
||||
如果希望忽略元组中一个或多个值,也可以使用 `_` 或 `..`,如 [“忽略模式中的值”][ignoring-values-in-a-pattern] 部分所示。如果问题是模式中有太多的变量,则解决方法是通过去掉变量使得变量数与元组中元素数相等。
|
||||
为了修复这个错误,可以使用 `_` 或 `..` 来忽略元组中一个或多个值,如 [“忽略模式中的值”][ignoring-values-in-a-pattern] 部分所示。如果问题是模式中有太多的变量,则解决方法是通过去掉变量使得变量数与元组中元素数相等。
|
||||
|
||||
### 函数参数
|
||||
|
||||
|
@ -2,7 +2,7 @@
|
||||
|
||||
> [ch18-02-refutability.md](https://github.com/rust-lang/book/blob/main/src/ch18-02-refutability.md)
|
||||
> <br>
|
||||
> commit d44317c3122b44fb713aba66cc295dee3453b24b
|
||||
> commit 0c2d41e2afce734825c3a12087d423e8c2f0ae53
|
||||
|
||||
模式有两种形式:refutable(可反驳的)和 irrefutable(不可反驳的)。能匹配任何传递的可能值的模式被称为是 **不可反驳的**(*irrefutable*)。一个例子就是 `let x = 5;` 语句中的 `x`,因为 `x` 可以匹配任何值所以不可能会失败。对某些可能的值进行匹配会失败的模式被称为是 **可反驳的**(*refutable*)。一个这样的例子便是 `if let Some(x) = a_value` 表达式中的 `Some(x)`;如果变量 `a_value` 中的值是 `None` 而不是 `Some`,那么 `Some(x)` 模式不能匹配。
|
||||
|
||||
|
@ -1,9 +1,10 @@
|
||||
## 所有的模式语法
|
||||
|
||||
> [ch18-03-pattern-syntax.md](https://github.com/rust-lang/book/blob/main/src/ch18-03-pattern-syntax.md) > <br>
|
||||
> commit e72de80f114dc68f69f3920768314f7c005d0dd5
|
||||
> [ch18-03-pattern-syntax.md](https://github.com/rust-lang/book/blob/main/src/ch18-03-pattern-syntax.md)
|
||||
> <br>
|
||||
> commit 6fce661a0938aa0da06526e7b8f98fd7e67a222f
|
||||
|
||||
通过本书我们已领略过许多不同类型模式的例子。在本节中,我们收集了模式中所有有效的语法,并讨论为什么以及何时你可能要使用这些语法。
|
||||
在本节中,我们收集了模式中所有有效的语法,并讨论为什么以及何时你可能要使用这些语法。
|
||||
|
||||
### 匹配字面值
|
||||
|
||||
@ -39,7 +40,7 @@
|
||||
|
||||
### 多个模式
|
||||
|
||||
在 `match` 表达式中,可以使用 `|` 语法匹配多个模式,它代表 **或**(_or_)的意思。例如,如下代码将 `x` 的值与匹配分支相比较,第一个分支有 **或** 选项,意味着如果 `x` 的值匹配此分支的任一个值,它就会运行:
|
||||
在 `match` 表达式中,可以使用 `|` 语法匹配多个模式,它代表 **或**(_or_)运算符模式。例如,如下代码将 `x` 的值与匹配分支相比较,第一个分支有 **或** 选项,意味着如果 `x` 的值匹配此分支的任一个值,它就会运行:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/no-listing-02-multiple-patterns/src/main.rs:here}}
|
||||
@ -49,15 +50,15 @@
|
||||
|
||||
### 通过 `..=` 匹配值的范围
|
||||
|
||||
`..=` 语法允许你匹配一个闭区间范围内的值。在如下代码中,当模式匹配任何在此范围内的值时,该分支会执行:
|
||||
`..=` 语法允许你匹配一个闭区间范围内的值。在如下代码中,当模式匹配任何在给定范围内的值时,该分支会执行:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/no-listing-03-ranges/src/main.rs:here}}
|
||||
```
|
||||
|
||||
如果 `x` 是 1、2、3、4 或 5,第一个分支就会匹配。这相比使用 `|` 运算符表达相同的意思更为方便;相比 `1..=5`,使用 `|` 则不得不指定 `1 | 2 | 3 | 4 | 5`。相反指定范围就简短的多,特别是在希望匹配比如从 1 到 1000 的数字的时候!
|
||||
如果 `x` 是 1、2、3、4 或 5,第一个分支就会匹配。这个语法在匹配多个值时相比使用 `|` 运算符来表达相同的意思更为方便;如果使用 `|` 则不得不指定 `1 | 2 | 3 | 4 | 5`。相反指定范围就简短的多,特别是在希望匹配比如从 1 到 1000 的数字的时候!
|
||||
|
||||
范围只允许用于数字或 `char` 值,因为编译器会在编译时检查范围不为空。`char` 和 数字值是 Rust 仅有的可以判断范围是否为空的类型。
|
||||
编译器会在编译时检查范围不为空,而 `char` 和数字值是 Rust 仅有的可以判断范围是否为空的类型,所以范围只允许用于数字或 `char` 值。
|
||||
|
||||
如下是一个使用 `char` 类型值范围的例子:
|
||||
|
||||
@ -65,7 +66,7 @@
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/no-listing-04-ranges-of-char/src/main.rs:here}}
|
||||
```
|
||||
|
||||
Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII letter`。
|
||||
Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASCII letter`。
|
||||
|
||||
### 解构并分解值
|
||||
|
||||
@ -83,9 +84,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
<span class="caption">示例 18-12: 解构一个结构体的字段为单独的变量</span>
|
||||
|
||||
这段代码创建了变量 `a` 和 `b` 来匹配结构体 `p` 中的 `x` 和 `y` 字段。这个例子展示了模式中的变量名不必与结构体中的字段名一致。不过通常希望变量名与字段名一致以便于理解变量来自于哪些字段。
|
||||
|
||||
因为变量名匹配字段名是常见的,同时因为 `let Point { x: x, y: y } = p;` 包含了很多重复,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称。示例 18-13 展示了与示例 18-12 有着相同行为的代码,不过 `let` 模式创建的变量为 `x` 和 `y` 而不是 `a` 和 `b`:
|
||||
这段代码创建了变量 `a` 和 `b` 来匹配结构体 `p` 中的 `x` 和 `y` 字段。这个例子展示了模式中的变量名不必与结构体中的字段名一致。不过通常希望变量名与字段名一致以便于理解变量来自于哪些字段。因为变量名匹配字段名是常见的,同时因为 `let Point { x: x, y: y } = p;` 包含了很多重复,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称。示例 18-13 展示了与示例 18-12 有着相同行为的代码,不过 `let` 模式创建的变量为 `x` 和 `y` 而不是 `a` 和 `b`:
|
||||
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
@ -115,9 +114,11 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
在这个例子中,值 `p` 因为其 `x` 包含 0 而匹配第二个分支,因此会打印出 `On the y axis at 7`。
|
||||
|
||||
记住 `match` 表达式一旦找到一个匹配的模式就会停止检查其它分支,所以即使 `Point { x: 0, y: 0}` 在 `x` 轴上也在 `y` 轴上,这些代码也只会打印 `On the x axis at 0`。
|
||||
|
||||
#### 解构枚举
|
||||
|
||||
本书之前的部分曾经解构过枚举,比如第六章中示例 6-5 中解构了一个 `Option<i32>`。一个当时没有明确提到的细节是解构枚举的模式需要对应枚举所定义的储存数据的方式。让我们以示例 6-2 中的 `Message` 枚举为例,编写一个 `match` 使用模式解构每一个内部值,如示例 18-15 所示:
|
||||
本书之前曾经解构过枚举(例如第六章示例 6-5),不过当时没有明确提到解构枚举的模式需要对应枚举所定义的储存数据的方式。让我们以示例 6-2 中的 `Message` 枚举为例,编写一个 `match` 使用模式解构每一个内部值,如示例 18-15 所示:
|
||||
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
@ -137,9 +138,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
#### 解构嵌套的结构体和枚举
|
||||
|
||||
目前为止,所有的例子都只匹配了深度为一级的结构体或枚举。当然也可以匹配嵌套的项!
|
||||
|
||||
例如,我们可以重构列表 18-15 的代码来同时支持 RGB 和 HSV 色彩模式:
|
||||
目前为止,所有的例子都只匹配了深度为一级的结构体或枚举,不过当然也可以匹配嵌套的项!例如,我们可以重构列表 18-15 的代码在 `ChangeColor` 消息中同时支持 RGB 和 HSV 色彩模式,如示例 18-16 所示:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-16/src/main.rs}}
|
||||
@ -167,7 +166,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
#### 使用 `_` 忽略整个值
|
||||
|
||||
我们已经使用过下划线(`_`)作为匹配但不绑定任何值的通配符模式了。虽然 `_` 模式作为 `match` 表达式最后的分支特别有用,也可以将其用于任意模式,包括函数参数中,如示例 18-17 所示:
|
||||
我们已经使用过下划线作为匹配但不绑定任何值的通配符模式了。虽然这作为 `match` 表达式最后的分支特别有用,也可以将其用于任意模式,包括函数参数中,如示例 18-17 所示:
|
||||
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
@ -179,7 +178,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
这段代码会完全忽略作为第一个参数传递的值 `3`,并会打印出 `This code only uses the y parameter: 4`。
|
||||
|
||||
大部分情况当你不再需要特定函数参数时,最好修改签名不再包含无用的参数。在一些情况下忽略函数参数会变得特别有用,比如实现 trait 时,当你需要特定类型签名但是函数实现并不需要某个参数时。此时编译器就不会警告说存在未使用的函数参数,就跟使用命名参数一样。
|
||||
大部分情况当你不再需要特定函数参数时,最好修改签名不再包含无用的参数。在一些情况下忽略函数参数会变得特别有用,比如实现 trait 时,当你需要特定类型签名但是函数实现并不需要某个参数时。这样可以避免一个存在未使用的函数参数的编译警告,就跟使用命名参数一样。
|
||||
|
||||
#### 使用嵌套的 `_` 忽略部分值
|
||||
|
||||
@ -205,9 +204,9 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
这会打印出 `Some numbers: 2, 8, 32`,值 4 和 16 会被忽略。
|
||||
|
||||
#### 通过在名字前以一个下划线开头来忽略未使用的变量
|
||||
#### 通过在名字前以一个 `_` 开头来忽略未使用的变量
|
||||
|
||||
如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个 bug。但是有时创建一个还未使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头。示例 18-20 中创建了两个未使用变量,不过当编译代码时只会得到其中一个的警告:
|
||||
如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为未使用的变量可能会是个 bug。但是有时创建一个还未使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头。示例 18-20 中创建了两个未使用变量,不过当编译代码时只会得到其中一个的警告:
|
||||
|
||||
<span class="filename">文件名:src/main.rs</span>
|
||||
|
||||
@ -217,7 +216,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
<span class="caption">示例 18-20: 以下划线开始变量名以便去掉未使用变量警告</span>
|
||||
|
||||
这里得到了警告说未使用变量 `y`,不过没有警告说未使用下划线开头的变量。
|
||||
这里得到了警告说未使用变量 `y`,不过没有警告说使用 `_x`。
|
||||
|
||||
注意,只使用 `_` 和使用以下划线开头的名称有些微妙的不同:比如 `_x` 仍会将值绑定到变量,而 `_` 则完全不会绑定。为了展示这个区别的意义,示例 18-21 会产生一个错误。
|
||||
|
||||
@ -239,7 +238,7 @@ Rust 知道 `c` 位于第一个模式的范围内,并会打印出 `early ASCII
|
||||
|
||||
#### 用 `..` 忽略剩余值
|
||||
|
||||
对于有多个部分的值,可以使用 `..` 语法来只使用部分并忽略其它值,同时避免不得不每一个忽略值列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分。在示例 18-23 中,有一个 `Point` 结构体存放了三维空间中的坐标。在 `match` 表达式中,我们希望只操作 `x` 坐标并忽略 `y` 和 `z` 字段的值:
|
||||
对于有多个部分的值,可以使用 `..` 语法来只使用特定部分并忽略其它值,同时避免不得不每一个忽略值列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分。在示例 18-23 中,有一个 `Point` 结构体存放了三维空间中的坐标。在 `match` 表达式中,我们希望只操作 `x` 坐标并忽略 `y` 和 `z` 字段的值:
|
||||
|
||||
```rust
|
||||
{{#rustdoc_include ../listings/ch18-patterns-and-matching/listing-18-23/src/main.rs:here}}
|
||||
@ -353,6 +352,6 @@ _at_ 运算符(`@`)允许我们在创建一个存放值的变量的同时测
|
||||
|
||||
## 总结
|
||||
|
||||
模式是 Rust 中一个很有用的功能,它帮助我们区分不同类型的数据。当用于 `match` 语句时,Rust 确保模式会包含每一个可能的值,否则程序将不能编译。`let` 语句和函数参数的模式使得这些结构更强大,可以在将值解构为更小部分的同时为变量赋值。可以创建简单或复杂的模式来满足我们的要求。
|
||||
模式是 Rust 中一个很有用的功能,它有助于我们区分不同类型的数据。当用于 `match` 语句时,Rust 确保模式会包含每一个可能的值,否则程序将不能编译。`let` 语句和函数参数的模式使得这些结构更强大,可以在将值解构为更小部分的同时为变量赋值。可以创建简单或复杂的模式来满足我们的要求。
|
||||
|
||||
接下来,在本书倒数第二章中,我们将介绍一些 Rust 众多功能中较为高级的部分。
|
||||
|
Loading…
Reference in New Issue
Block a user