diff --git a/book/contents/basic/match-pattern/all-patterns.md b/book/contents/basic/match-pattern/all-patterns.md index 5ae418c3..816593c7 100644 --- a/book/contents/basic/match-pattern/all-patterns.md +++ b/book/contents/basic/match-pattern/all-patterns.md @@ -1,6 +1,6 @@ # 全模式列表 -在本书中我们已领略过许多不同类型模式的例子. 本节的目标就是把这些模式语法都罗列出来,方便大家检索查阅。 +在本书中我们已领略过许多不同类型模式的例子,本节的目标就是把这些模式语法都罗列出来,方便大家检索查阅(模式匹配在我们的开发中会经常用到)。 ### 匹配字面值 @@ -15,7 +15,7 @@ match x { } ``` -这段代码会打印 `one` 因为 `x` 的值是 1。如果希望代码获得特定的具体值,则该语法很有用。 +这段代码会打印 `one` 因为 `x` 的值是 1,如果希望代码获得特定的具体值,那么这种语法很有用。 ### 匹配命名变量 @@ -38,13 +38,13 @@ fn main() { 让我们看看当 `match` 语句运行的时候发生了什么。第一个匹配分支的模式并不匹配 `x` 中定义的值,所以代码继续执行。 -第二个匹配分支中的模式引入了一个新变量 `y`,它会匹配任何 `Some` 中的值。因为我们在 `match` 表达式的新作用域中,这是一个新变量,而不是开头声明为值 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`。 +如果 `x` 的值是 `None` 而不是 `Some(5)`,头两个分支的模式不会匹配,所以会匹配模式`_`。这个分支的模式中没有引入变量 `x`,所以此时表达式中的 `x` 会是外部没有被覆盖的 `x`,也就是`Some(5)`。 一旦 `match` 表达式执行完毕,其作用域也就结束了,同理内部 `y` 的作用域也结束了。最后的 `println!` 会打印 `at the end: x = Some(5), y = 10`。 -如果你不想引入变量覆盖,那么需要使用匹配守卫(match guard)的方式,稍后在[匹配守卫提供的额外条件](#匹配守卫提供的额外条件)中会讲解。 +如果你不想引入变量覆盖,那么需要使用匹配守卫(match guard)的方式,稍后在[匹配守卫提供的额外条件](#匹配守卫提供的额外条件)中会讲解。 ### 单分支多模式 @@ -77,9 +77,9 @@ 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`。 ### 解构并分解值 @@ -118,9 +118,9 @@ fn main() { } ``` -这段代码创建了变量 `a` 和 `b` 来匹配结构体 `p` 中的 `x` 和 `y` 字段, 这个例子展示了**模式中的变量名不必与结构体中的字段名一致**。不过通常希望变量名与字段名一致以便于理解变量来自于哪些字段。 +这段代码创建了变量 `a` 和 `b` 来匹配结构体 `p` 中的 `x` 和 `y` 字段,这个例子展示了**模式中的变量名不必与结构体中的字段名一致**。不过通常希望变量名与字段名一致以便于理解变量来自于哪些字段。 -因为变量名匹配字段名是常见的,同时因为 `let Point { x: x, y: y } = p;` 包含了很多重复,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称。下例与上例有着相同行为的代码,不过 `let` 模式创建的变量为 `x` 和 `y` 而不是 `a` 和 `b`: +因为变量名匹配字段名是常见的,同时因为 `let Point { x: x, y: y } = p;` 中`x` 和 `y`重复了,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称。下例与上例有着相同行为的代码,不过 `let` 模式创建的变量为 `x` 和 `y` 而不是 `a` 和 `b`: ```rust struct Point { @@ -162,8 +162,7 @@ fn main() { 首先是`match`第一个分支,指定匹配`y`为`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`。 @@ -206,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` 的值来观察其他分支代码的运行。 @@ -216,7 +215,7 @@ fn main() { #### 解构嵌套的结构体和枚举 -目前为止,所有的例子都只匹配了深度为一级的结构体或枚举。当然也可以匹配嵌套的项! +目前为止,所有的例子都只匹配了深度为一级的结构体或枚举。 `match`也可以匹配嵌套的项! 例如使用下面的代码来同时支持 RGB 和 HSV 色彩模式: @@ -261,7 +260,7 @@ fn main() { #### 解构结构体和元组 -甚至可以用复杂的方式来混合、匹配和嵌套解构模式。如下是一个复杂结构体的例子,其中结构体和元组嵌套在元组中,并将所有的原始类型解构出来: +我们甚至可以用复杂的方式来混合、匹配和嵌套解构模式。如下是一个复杂结构体的例子,其中结构体和元组嵌套在元组中,并将所有的原始类型解构出来: ```rust struct Point { @@ -272,15 +271,15 @@ struct Point { let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10 }); ``` -这种将复杂类型分解匹配的方式,可以让我们单独得到感兴趣的值。 +这种将复杂类型分解匹配的方式,可以让我们单独得到感兴趣的某个值。 ### 忽略模式中的值 -有时忽略模式中的一些值是很有用的,比如在`match`中的最后一个分支使用`_`模式匹配剩余的所有值。 你也可以在另一个模式中使用 `_` 模式,使用一个以下划线开始的名称,或者使用 `..` 忽略所剩部分的值。 +有时忽略模式中的一些值是很有用的,比如在`match`中的最后一个分支使用`_`模式匹配所有剩余的值。 你也可以在另一个模式中使用 `_` 模式,使用一个以下划线开始的名称,或者使用 `..` 忽略所剩部分的值。 #### 使用 `_` 忽略整个值 -虽然 `_` 模式作为 `match` 表达式最后的分支特别有用,但是我们可以让它更有用。例如可以将其用于函数参数中: +虽然 `_` 模式作为 `match` 表达式最后的分支特别有用,但是它的作用还不限于此。例如可以将其用于函数参数中: ```rust @@ -299,7 +298,7 @@ fn main() { #### 使用嵌套的 `_` 忽略部分值 -可以在一个模式内部使用`_` 忽略部分值: +可以在一个模式内部使用`_` 忽略部分值: ```rust let mut setting_value = Some(5); @@ -324,7 +323,7 @@ println!("setting is {:?}", setting_value); 剩下的形如`(Some(_),None)`,`(None, Some(_))`, `(None,None)`形式,都由第二个分支`_`进行分配。 -还可以在一个模式中的多处使用下划线来忽略特定值,如下所示,这里忽略了一个五元元组中的第二和第四个值: +还可以在一个模式中的多处使用下划线来忽略特定值,如下所示,这里忽略了一个五元元组中的第二和第四个值: ```rust let numbers = (2, 4, 8, 16, 32); @@ -336,13 +335,13 @@ match numbers { } ``` -老生常谈:模式匹配一定要类型相同,因此匹配`numbers`元组的模式,也必须有五个值。 +老生常谈:模式匹配一定要类型相同,因此匹配`numbers`元组的模式,也必须有五个值(元组中元素的数量也属于元组类型的一部分)。 这会打印出 `Some numbers: 2, 8, 32`, 值 4 和 16 会被忽略。 #### 使用下划线开头忽略未使用的变量 -如果你创建了一个变量却不在任何地方使用它, Rust 通常会给你一个警告,因为这可能会是个 bug。但是有时创建一个还未使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头: +如果你创建了一个变量却不在任何地方使用它,Rust 通常会给你一个警告,因为这可能会是个bug。但是有时创建一个不会被使用的变量是有用的,比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头: ```rust fn main() { @@ -351,7 +350,7 @@ fn main() { } ``` -这里得到了警告说未使用变量 `y`,至于x则并没有警告。 +这里得到了警告说未使用变量 `y`,至于`x`则没有警告。 注意, 只使用 `_` 和使用以下划线开头的名称有些微妙的不同:比如 **`_x` 仍会将值绑定到变量,而 `_` 则完全不会绑定**。 @@ -364,7 +363,7 @@ if let Some(_s) = s { println!("{:?}", s); ``` -`s`是一个拥有所有权的动态字符串,在上面代码中,我们会得到一个错误,因为 `s` 的值会被转移给 `_s`, 在`println!`中再次使用`s`会报错: +`s`是一个拥有所有权的动态字符串,在上面代码中,我们会得到一个错误,因为 `s` 的值会被转移给 `_s`,在`println!`中再次使用`s`会报错: ```console error[E0382]: borrow of partially moved value: `s` --> src/main.rs:8:22 @@ -390,7 +389,7 @@ println!("{:?}", s); #### 用 `..` 忽略剩余值 -对于有多个部分的值,可以使用 `..` 语法来只使用部分并忽略其它值,同时避免不得不每一个忽略值列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分. +对于有多个部分的值,可以使用 `..` 语法来只使用部分值而忽略其它值,这样也不用再为每一个被忽略的值都单独列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分. ```rust struct Point { @@ -408,7 +407,7 @@ match origin { 这里列出了 `x` 值,接着使用了`..` 模式来忽略其它字段,这样的写法要比一一列出其它字段,然后用`_`忽略简洁的多。 -还可以用`..`来忽略中间的所有值: +还可以用`..`来忽略元组中间的某些值: ```rust fn main() { @@ -452,14 +451,14 @@ error: `..` can only be used once per tuple pattern // 每个元组模式只能 error: could not compile `world_hello` due to previous error ^^ ``` -Rust无法判断,`second`应该匹配`numbers`中的第几个元素,因此在这里使用两个`..`模式,是由很大歧义的! +Rust无法判断,`second`应该匹配`numbers`中的第几个元素,因此这里使用两个`..`模式,是由很大歧义的! ### 匹配守卫提供的额外条件 **匹配守卫**(*match guard*)是一个位于 `match` 分支模式之后的额外 `if` 条件,它能为分支模式提供更进一步的匹配条件。 -这个条件可以使用模式中创建的变量: +这个条件可以使用模式中创建的变量: ```rust let num = Some(4); @@ -471,13 +470,13 @@ 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` 成员。 因为模式中无法提供类如`if x < 5`的表达能力,所以我们通过匹配守卫的方式来实现。 -在之前, 我们提到可以使用匹配守卫来解决模式中变量覆盖的问题,那里 `match` 表达式的模式中新建了一个变量而不是使用 `match` 之外的同名变量。新变量意味着不能够测试外部变量的值,下面代码展示了如何使用匹配守卫修复这个问题。 +在之前,我们提到可以使用匹配守卫来解决模式中变量覆盖的问题,那里 `match` 表达式的模式中新建了一个变量而不是使用 `match` 之外的同名变量。内部变量覆盖了外部变量,意味着不能够使用外部变量的值,下面代码展示了如何使用匹配守卫修复这个问题。 ```rust fn main() { @@ -512,9 +511,9 @@ match x { 这个匹配条件表明此分支只匹配 `x` 值为 `4`、`5` 或 `6` **同时** `y` 为 `true` 的情况。 -虽然在第一个分支中,`x`匹配了模式`4`,但是对于匹配守卫`if y`来说,因为`y`是`false`,因此该守卫条件的值永远是`false`,也意味着第一个分支永远无法被匹配. +虽然在第一个分支中,`x`匹配了模式`4`,但是对于匹配守卫`if y`来说,因为`y`是`false`,因此该守卫条件的值永远是`false`,也意味着第一个分支永远无法被匹配。 -下面的文字图解释了匹配守卫作用于多个模式时的优先级规则,第一张是正确的: +下面的文字图解释了匹配守卫作用于多个模式时的优先级规则,第一张是正确的: ```text (4 | 5 | 6) if y => ... @@ -579,7 +578,7 @@ fn main() { num @ (1 | 2) ``` -但是,如果你用的是Rust1.53之前的版本,那该写法会报错,因为编译器不支持。 +但是,如果你用的是Rust1.53之前的版本,那这种写法会报错,因为编译器不支持。 至此,模式匹配的内容已经全部完结,复杂但是详尽,想要一次性全部记住属实不易,因此读者可以先留一个印象,等未来需要时,再来翻阅寻找具体的模式实现方式。