Updated 'src/patterns/syntax.md'.

This commit is contained in:
Hector PENG
2026-04-20 17:11:44 +08:00
parent d706f0acac
commit 7cb8292442
2 changed files with 90 additions and 101 deletions

View File

@@ -1,7 +1,25 @@
fn main() {
let x = 5 else {
return;
};
println! ("{x}");
enum Color {
Rgb(u32, u32, u32),
Hsv(u32, u32, u32),
}
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(Color),
}
fn main() {
let msg = Message::ChangeColor(Color::Hsv(0, 160, 255));
match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => {
println! ("将颜色改为红 {r}、绿 {g} 及蓝 {b}");
}
Message::ChangeColor(Color::Hsv(h, s, v)) => {
println! ("将颜色改为色调 {h}、饱和度 {s} 及颜色值 {v}");
}
_ => (),
}
}

View File

@@ -1,17 +1,11 @@
# 模式语法
**Pattern Syntax**
在这个小节中,咱们会聚齐模式方面的全部有效语法,并讨论因何及何时会打算使用这每种的语法。
在这一小节中,我们汇总所有模式方面的有效语法,并讨论咱们可能打算使用每种语法的原因和时机。
## 匹配字面值
**Matching Literals**
正如咱们在第 6 章中曾看到的那样,咱们可以直接将模式与字面值匹配。下面的代码给到了一些示例:
正如咱们在第 6 章中看到的,咱们可以直接将模式与字面值匹配。下面的代码给出了一些示例:
```rust
@@ -25,16 +19,14 @@
}
```
由于 `x` 里的值`1`,此代码会打印出 ``。当咱们打算代码在其获取到某个特定具体值而采取某种动作时,这种语法就是有用
这段代码会打印 ``,因`x` 中的值为 `1`。当咱们希望代码在其获取到某个特定具体值时执行某项操作时,这种语法非常有用。
## 匹配命名变量
**Matching Named Variables**
命名变量属于匹配任意值的不可证伪模式,同时咱们已在本书中,用到他们许多次了。不过,当咱们在 `match` 表达式中使用命名变量时,便有着一种复杂性。由于 `match` 关键字开启了一个新的作用域,作用模式部分,而该 `match` 表达式内部声明出的那些变量,将遮蔽该 `match` 结构the `match` construct 外部那些有着同意名字的变量,这与所有变量下的情况一样。在下面清单 18-11 中,咱们以值 `Some(5)` 声明了名为 `x` 的一个变量,及有着值 `10` 的一个变量 `y`。随后咱们在值 `x` 上创建了一个 `match` 表达式。请注意那些匹配支臂中的模式与末尾处的 `println!`,并在运行此代码或阅读接下来的内容前,尝试得出该代码将打印出什么。
命名变量属于匹配任何值的不可证伪模式,我们在本书中已经多次使用他们。但是,当咱们在 `match``if let``while let` 表达式中使用命名变量时,会存在一定的复杂性。由于这三类表达式都会开启新的作用域,因此作为这些表达式内的模式一部分而声明的变量,会像所有变量一样,遮蔽这些结构外部同名的变量。在下面清单 19-11 中,我们通过 `Some(5)` 声明了一个名为 `x` 的变量,以及有着值 `10` 的变量 `y`。然后,我们对值 `x` 上创建一个 `match` 表达式。请仔细观察匹配支臂中的模式及末尾的 `println!`,并在运行这段代码或继续阅读前,尝试弄清楚这段代码将打印什么。
<a name="listing_19-11"></a>
文件名:`src/main.rs`
```rust
@@ -42,34 +34,30 @@
let y = 10;
match x {
Some(50) => println! ("得到 50"),
Some(50) => println! ("得到 50"),
Some(y) => println! ("已匹配y = {y}"),
_ => println! ("默认情况x = {:?}", x),
_ => println! ("默认情况x = {x:?}"),
}
println! ("最后x = {:?}, y = {y}", x);
println! ("最后x = {x:?}, y = {y}");
```
*清单 18-11:有着引入了遮蔽变量 `y` 的一条支臂的 `match` 表达式*
**清单 19-11**:带有一个引入新变量的支臂的 `match` 表达式,该变量遮蔽了现有变量 `y`
下面就来走一遍,在这个 `match` 表达式运行时会发生什么。个匹配支臂中的模式不会匹配 `x` 定义值,因此代码继续。
我们来逐步分析这个 `match` 表达式运行时会发生什么。第一个匹配支臂中的模式匹配 `x` 中的定义值,因此代码继续。
第二个匹配支臂中引入了名为 `y` 新变量的那个模式,将匹配某个 `Some` 值内部的任值。由于咱们是在这个 `match` 表达式内部的新作用域中,因此这是个新的 `y` 变量,而不再是开头的以值 `10` 定义`y`。这个新的 `y` 绑定将匹配某个 `Some`的任值,那便是咱们在 `x`所拥有的那个值了。因此,这个新 `y` 绑定到 `x`那个 `Some` 的内层值。那个值为 `5`,因此那个支臂的表达式就会执行,并打印 `已匹配y = 5`
第二个匹配支臂中的模式引入了一个名为 `y` 新变量,该变量将匹配 `Some` 值内部的任值。由于我们处于 `match` 表达式内部的新作用域中,因此这是个新的 `y` 变量,而不是我们在开头声明的值为 `10``y`。这个新的 `y` 绑定将匹配 `Some` 内的任值,这正是我们在 `x`的值。因此,这个新 `y` 绑定到 `x``Some` 的内层值。值为 `5`,因此这一支臂的表达式执行,并打印 `已匹配y = 5`
而若 `x` `None` 值而 `Some(5)`,那么两个支臂中的模式,就都不会匹配,该值将与其中的下划线 `_` 匹配。咱们并未以那个下划线模式引入这个 `x` 变量,因此表达式中的 `x`未被遮蔽的外层 `x`在这个假定情况中,该 `match` 将打印 `默认情况x = None`
`x``None` 值而不是 `Some(5)`,那么两个支臂中的模式都不会匹配,因此该值将与下划线 `_` 匹配。我们没有在下划线模式引入变量 `x`,因此表达式中的 `x`然时未被遮蔽的外层 `x`。在这种假设的情况下,`match` 将打印 `默认情况x = None`
在这个 `match` 表达式完成是,他的作用域结束,而内层作用域`y` 也结束。最后的 `println!` 产生 `最后x = Some(5), y = 10`
`match` 表达式执行完毕后,他的作用域结束,而内层的 `y` 的作用域也结束。最后的 `println!` 产生 `最后x = Some(5), y = 10`
创建比较外层作用域中 `x` `y` 值的一个 `match` 表达式,而非引入一个遮蔽变量,咱们将需要使用某种匹配卫条件a match guard conditional。稍后咱们将在 [“带有匹配保护的额外条件](#使用匹配卫兵的额外条件extra-conditionals-with-match-guards) 小节,讨论到匹配保护问题。
创建一个比较外层 `x` `y` 值的 `match` 表达式,而非引入一个遮蔽现有变量 `y` 的新变量,我们需要改用匹配卫语句条件a match guard conditional。我们稍后将在 [通过匹配卫语句添加条件](#通过匹配卫语句添加条件) 小节,讨论到匹配保护问题。
## 匹配多个模式
## 多个模式
**Multiple Patterns**
`match` 表达式中,咱们可以使用 `|` 语法,即模式 *or* 运算符,匹配多个模式。比如,在下面的代码中,咱们把 `x` 的值与那些匹配支臂匹配,其中头一个支臂就有一个 *or* 选项,表示在 `x` 的值与那条支臂中两个值之一匹配时,那条支臂的代码都将运行:
`match` 表达式中,咱们可以使用 `|` 语法,即模式 ** 运算符,匹配多个模式。例如,在下面的代码中,我们将 `x` 的值与匹配支臂匹配,其中第一个支臂有着一个 ** 选项,这意味着当 `x` 的值匹配该支臂中的任何一个值时,这一支臂的代码将运行:
```rust
@@ -82,15 +70,12 @@
}
```
代码打印 `一或二`
这段代码打印 `一或二`
## 使用 `..=` 匹配值范围
## 通过 `..=` 匹配值范围
**Matching Ranges of Values with `..=`**
这种 `..=` 语法允许咱们与某个包容性值范围匹配match to an inclusive range of values。下面的代码中当某个模式匹配给定范围中任何值时那条支臂便会执行
`..=` 语法允许我们匹配包含起始值和结束值的范围匹配。在下面的代码中,当模式匹配给定范围内的任意值时,该支臂将执行:
```rust
let x = 5;
@@ -101,12 +86,11 @@
}
```
`x``1, 2, 3, 4``5` 时,一条支臂将匹配。相比于使用 `|` 运算符,对于多个匹配值,这种语法更便于表达同样的概念;若咱们使用的是 `|`,那么将不得不指明 `1 | 2 | 3 | 4 | 5`而指明一个范围简短多,尤其是在打算匹配比如任何 `1``1000` 之间的数字时!
`x``1, 2, 3, 4``5` 时,一条支臂将匹配。相比于使用 `|` 运算符,这种语法对于多个匹配值更方便;若使用 `|`,我们就必须指定 `1 | 2 | 3 | 4 | 5`指定范围简短多,尤其是当我们想要匹配 1 到 1000 之间的任何数字时!
编译器会在编译时检查范围为空,而由于 Rust 可识别出某个范围为空或不为空的类型,就只有 `char` 与数字值,因此就只运行数字`char`两种范围
编译器会在编译时检查范围是否为空,而由于 Rust 仅能判断 `char` 和数值类型的范围是否为空,因此范围仅允许带有数值`char` 值。
下面是使用 `char` 值范围的一个示例:
下面是个使用 `char` 值范围的示例:
```rust
let x = 'c';
@@ -118,25 +102,19 @@
}
```
Rust 能分辨出 `c` 是在头一个模式的范围,并打印 `靠前 ASCII 字母`
Rust 能分辨出 `c` 属于第一个模式的范围,并打印 `靠前 ASCII 字母`
## 将值拆散的解构
## 解构以拆分值
**Destructuring to Break Apart Values**
我们还可以使用模式来解构结构体、枚举和元组,从而使用这些值的不同部分。我们来逐一分析这些值。
咱们还可以运用模式,来解构结构体、枚举及元组,从而用到这些值的不同部分。下面就来贯穿这各个的值。
### 解构结构体
**Destructuring Stucts**
下面清单 18-12 给出了咱们可使用带有一个 `let` 语句的模式,而予以拆散的、有着两个字段,`x``y` 的一个 `Point` 结构体。
### 结构体
下面清单 19-12 展示了个具有两个字段 `x``y``Point` 结构体,我们可以与 `let` 语句一起使用模式拆解他。
<a name="listing_19-12"></a>
文件名:`src/main.rs`
```rust
@@ -155,11 +133,11 @@ fn main() {
}
```
*清单 18-12:将结构体的那些字段解构为一些单独变量*
这段代码创建出匹配结构体 `p``x``y` 字段的变量 `a``b`。此示例展示了模式中变量的名字,不必匹配结构体的字段名字。但是,将变量名字与字段相匹配,以令到更易与记住哪些变量来自那个字段,则是通常做法。由于这种普遍用法,同时由于写下 `let Point { x: x, y: y } = p;`包含了很多重复Rust 便有了匹配结构体字段模式的一种简写:咱们只需列出结构体字段的名字,那么自该模式创建出的那些变量,就将有着这些同样名字。下面清单会与清单 18-12 中的代码,以同样方式行事,不过在那个 `let` 模式中创建出的变量,为 `x``x`,而不再是 `a``b` 了。
**清单 19-12**:解构结构体的字段为单独变量
这段代码创建了变量 `a``b`,匹配结构体 `p` 中的 `x``y` 字段的值。这个示例表明,模式中的变量名字不必与结构体的字段名字一致。但是,通常会将变量名字与字段名字保持一致,以便更容易记住哪些变量来自哪个字段。由于这种常见用法,并且写下 `let Point { x: x, y: y } = p;` 会包含大量重复Rust 为匹配结构体字段的模式提供了一种简写形式:咱们只需列出结构体字段的名字,从该模式创建的变量就会具有同样的名字。下面清单 19-13 的行为与清单 19-12 中的代码相同,但在 `let` 模式中创建的变量是 `x``x`,而不是 `a``b`
<a name="listing_19-13"></a>
文件名:`src/main.rs`
```rust
@@ -178,15 +156,19 @@ fn main() {
}
```
*清单 18-12用结构体字段简写解构结构体字段*
**清单 19-13**:使用结构体字段简写解构结构体字段
代码创建了变量 `p` `x` `y` 字段相匹配的变量 `x` `y`。结果便是变量 `x``y` 包含着来自结构体 `p`那些值。
这段代码创建了变量 `x` `y`,匹配变量 `p` 中的字段 `x` `y`。结果是变量 `x``y` 包含结构体 `p` 的值。
咱们也能以一些字面值,作为结构体模式部分,而非创建出所有字段的变量,而加以解构。这样做允许咱们在创建出一些变量解构其他字段的同时,测试些字段。
我们还可以作为结构体模式部分的字面值解构,而无需为所有字段都创建变量。这样做允许在创建变量解构其他字段的同时,针对特定值测试些字段。
在下面清单 19-14 中,我们有个 `match` 表达式,将 `Point` 值分为三种情形:
在下面清单 18-14 中,咱们有着一个将 `Point` 值分离到三种情形的一个 `match` 表达式:直接位于 `x` 轴上的那些点( `y = 0`此模式为真)、`y` 轴上的那些点,或既不在 `x` 也不在 `y` 轴上的那些点。
- 直接位于 `x` 轴上的点( `y = 0` 时为真)、
- 位于 `y` 轴上的点,
- 或不在任一轴上的点。
<a name="listing_19-14"></a>
文件名:`src/main.rs`
```rust
@@ -201,24 +183,22 @@ fn main() {
}
```
*清单 18-14同时在一个模式中解构与字面值匹配*
**清单 19-14**:在一个模式中解构和匹配字面值
首个支臂通过指 `y` 字段在其值与字面值 `0` 匹配时匹配,而将匹配位于 `x` 轴上任意点。该模式仍创建了咱们可在此支臂代码中用到的变量 `x`
第一条支臂通过指定当 `y` 字段的值匹配字面值 `0` 时匹配,而将匹配位于 `x` 轴上任意点。该模式仍创建一个 `x` 变量,我们可以在这一支臂代码中使用。
类似地,第二条支臂通过指 `x` 字段在其值为 `0` 时匹配,而匹配位于 `y` 轴上的任意点,同时创建处 `y` 字段值的一个变量 `y`。第三条支臂没有指定任何字面值,因此其会匹配全部其他 `Point`,并创建出 `x``y` 字段的两个变量。
同样地,第二条支臂通过指定当 `x` 字段值为 `0`该字段匹配,而匹配位于 `y` 轴上的任意点,并为 `y` 字段创建变量 `y`。第三条支臂指定任何字面值,因此他匹配任何其他 `Point`,并 `x``y` 字段创建变量。
示例中,`p`由于 `x` 包含着一个 `0`而匹配第二条支臂,从而此代码将打印 `在 y 轴的 -7 处`.
这个示例中,由于 `x` 包含 `0`因此值 `p` 与第二条支臂匹配,所以这段代码将打印 `在 y 轴的 -7 处`.
请记住 `match` 表达式一旦找到第一个匹配的模式,就会停止检查支臂,因此尽管 `Point { x: 0, y: 0 }` 是在 `x` 轴与 `y` 轴上,此代码将只打印 `在 x 轴的 0 处`
请记住`match` 表达式一旦找到第一个匹配的模式,就会停止检查支臂,因此尽管 `Point { x: 0, y: 0 }` 位于 `x` 轴与 `y` 轴上,但这段代码只会打印 `在 x 轴的 0 处`
### 解构枚举
### 枚举
**Destructuring Enums**
本书中咱们已经解构过枚举(比如,第 6 章中的清单 6-5但尚未明确讨论过以与存储在枚举内部数据所定义方式的相对应方式来解构某个枚举的模式。作为一个示例在下面清单 18-15 中,咱们使用清单 6-2 中的那个 `Message` 枚举,并编写了带有将解构各个内部值的一个 `match` 表达式。
我们在本书中解构过枚举(比如,第 6 章中的 [清单 6-5](../enums_and_pattern_matching/match_control_flow.md#listing_6-5)),但我们还没有明确讨论解构枚举的模式,与枚举中存储的数据的定义方式相对应。例如,在下面清单 19-15 中,我们使用 [清单 6-2](../enums_and_pattern_matching/defining_an_enum.md#listing_6-2) 中的 `Message` 枚举,并编写了个有着模式的 `match` 表达式,将解构每个内层值。
<a name="listing_19-15"></a>
文件名:`src/main.rs`
```rust
@@ -243,30 +223,28 @@ fn main() {
println! ("文本消息:{text}");
}
Message::ChangeColor(r, g, b) => {
println! ("颜色为 红 {r},绿 {g},和蓝 {b}");
println! ("修改颜色为 红 {r},绿 {g},和蓝 {b}");
}
}
}
```
*清单 18-15:解构保存着不同类别值的枚举变种*
**清单 19-15**:解构包含不同类别值的枚举变种
代码将打印 `颜色为 红 0绿 160和蓝 255`。请尝试修改 `msg` 的值,来看到该代码自其他支臂运行。
这段代码将打印 `修改颜色为 红 0绿 160和蓝 255`。请尝试修改 `msg` 的值,看看其他支臂中的代码运行。
应不带任何数据的那些枚举变种,像是 `Message::Quit`咱们就无法进一步解构值。们只能匹配字面的 `Message::Quit` 值,且在那个模式中没有变量。
于没有任何数据的枚举变种,比如 `Message::Quit`我们无法进一步解构值。们只能匹配字面`Message::Quit` 值,并且这一模式中没有变量。
对于类似结构体的枚举变量,好比 `Message::Move`们可以使用类似于指明用于匹配结构体的那种模式。在变种名字之后,们放置了一对花括号,并在随后列出有着变量的那些字段,从而咱们就拆散了要在此支臂代码中用到的各个部分。这里咱们运用了曾在清单 18-13 中曾用过的简写形式。
对于类似结构体的枚举变种,比如 `Message::Move`们可以使用与我们对结构体指定的类似模式。在变种名字之后,们放置了一对花括号,然后通过变量列出字段,以便我们分解各个部分已在这一支臂代码中使用。这里我们使用了与清单 19-13 中一样的简写形式。
对于类似元组的那些枚举变种,好比保存着有一个元素元组 `Message::Write` 与保存着有三个元素元组的 `Message::ChangeColor`模式便于指定用于匹配元组的模式类似。模式中的变量个数,务必要与咱们所匹配的变种中元素个数相匹配
对于类似元组的枚举变种,比如包含一个元素元组 `Message::Write` 和包含三个元素元组的 `Message::ChangeColor`模式与我们用于匹配元组的模式类似。模式中的变量数量必须要与我们要匹配的变种中元素数量一致
### 嵌套结构体与枚举的解构
### 嵌套结构体与枚举
**Destructuring Nested Structs and Enums**
到目前为止,咱们的这些示例都匹配的是一层深的结构体与枚举,而匹配也是能够在嵌套项目上工作的!比如,咱们可将清单 18-15 中的代码,重构为在 `ChangeColor` 消息中,支持 RGB 与 HSV 两种颜色,如下清单 18-16 中所示:
到目前为止,我们的示例都只匹配了一层深度的结构体与枚举,但匹配也可以用于嵌套项目!例如,我们可以重构清单 19-15 中的代码,以支持 `ChangeColor` 消息中的 RGB 与 HSV 颜色,如下清单 19-16 中所示。
<a name="listing_19-16"></a>
文件名:`src/main.rs`
```rust
@@ -275,7 +253,7 @@ enum Color {
Hsv(u32, u32, u32),
}
emum Message {
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
@@ -287,44 +265,37 @@ fn main() {
match msg {
Message::ChangeColor(Color::Rgb(r, g, b)) => {
println! ("将颜色改为{r}、绿 {g} 及蓝 {b}");
println! ("修改颜色为:{r}、绿 {g} 及蓝 {b}");
}
Message::ChangeColor(Color::Hsv(h, s, v)) => {
println! ("颜色改为色调 {h}、饱和度 {s}颜色值 {v}");
println! ("修改颜色改为色调 {h}、饱和度 {s}颜色值 {v}");
}
_ => (),
}
}
```
*清单 18-16嵌套枚举的匹配*
**清单 19-16**:对嵌套枚举的匹配
`match` 表达式中第一个支臂的模式,匹配包含 `Color::Rgb` 变种的 `Message::ChangeColor` 枚举变种;然后,该模式会绑定到三个内部 `i32` 值。第二支臂的模式同样匹配 `Message::ChangeColor` 枚举变种,但内部枚举匹配的是 `Color::Hsv`。即使涉及两个枚举,我们也可以在一个 `match` 表达式中指定这些复杂条件。
`match` 表达式中首个支臂的模式,匹配包含着 `Color::Rgb` 变种的 `Message::ChangeColor` 枚举变种;随后该模式绑定到那三个内部的 `i32` 值。第二支臂的模式,同样匹配的是 `Message::ChangeColor` 枚举变种,只不过那内部枚举匹配的是 `Color::Hsv` 了。咱们可在一个 `match` 表达式中,指定这些复杂条件,即使涉及到两个枚举。
### 结构体和元组
### 解构结构体与元组
**Destructing Structs and Tuples**
咱们甚至可以更复杂的方式,对解构模式进行混用、匹配及嵌套。下面的示例,给出了一种复杂的解构,其中在一个元组中,嵌套了结构体与元组,并讲全部原生值解构了出来:
咱们可以更复杂的方式混合、匹配并嵌套解构模式。以下示例展示了一个复杂的解构,其中我们将结构体与元组嵌套在元组内,并解构出所有原始值:
```rust
let ((feet, inches), Point {x, y}) = ((3, 10), Point { x: 3, y: -10});
```
此代码实现将复杂类型,拆分为其各个组部分,从而们可单独使用咱们所感兴趣的那些值。
这段代码让我们可以将复杂类型分解为各个组部分,从而们可单独使用我们感兴趣的值。
模式解构数据,是各自独立使用诸如结构体中个字段值,此类各部分值的一种便利方式
通过模式解构,是一种使用彼此分开使用值的部分的便捷方式,例如结构体中个字段中的值。
## 忽略模式中的值
**Ignoring Values in a Pattern**
咱们已经看到,某些时候忽略模式中的一些值是有用的,比如在为获取到不具体完成任何事情,而确实处理全部剩余可能值的捕获全部的 `match` 最后支臂中。有少数几种忽略模式中全部或部分值的方式:使用 `_` 模式the `_` pattern, 咱们已经见到过)、在另一模式中使用 `_` 模式、使用以下划线开头的名字,或使用 `..` 来忽略某个值的其余部分。下面就来探讨,怎样及为何要使用各个的这些模式。