Merge pull request #4 from KaiserY/main

Update.
pull/869/head
须语 2 months ago committed by GitHub
commit 949fc30f54
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -4,13 +4,12 @@
## 状态
- ch17 async & await 施工完毕。
- 2024 edtion 施工中,若示例代码有误请见谅。
- 2024 edtion 施工完毕。
PS:
* 对照源码位置:[https://github.com/rust-lang/book/tree/main/src][source]
* 每章翻译开头都带有官方链接和 commit hash若发现与官方不一致欢迎 Issue 或 PR :)
* 每章翻译开头都带有官方链接和 commit hash 的注释,若发现与官方不一致,欢迎 Issue 或 PR
[source]: https://github.com/rust-lang/book/tree/main/src

@ -1,7 +1,6 @@
# 附录
> [appendix-00.md](https://github.com/rust-lang/book/blob/main/src/appendix-00.md)
> <br>
> commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f
<!-- https://github.com/rust-lang/book/blob/main/src/appendix-00.md -->
<!-- commit 1fedfc4b96c2017f64ecfcf41a0a07e2e815f24f -->
附录部分包含一些在你的 Rust 之旅中可能用到的参考资料。

@ -1,81 +1,81 @@
## 附录 A关键字
> [appendix-01-keywords.md](https://github.com/rust-lang/book/blob/main/src/appendix-01-keywords.md)
> <br>
> commit efbafdba3618487fbc9305318fcab9775132ac15
<!-- https://github.com/rust-lang/book/blob/main/src/appendix-01-keywords.md -->
<!-- commit 56ec353290429e6547109e88afea4de027b0f1a9 -->
下面的列表包含 Rust 中正在使用或者以后会用到的关键字。因此,这些关键字不能被用作标识符(除了 “[原始标识符][raw-identifiers]” 部分介绍的原始标识符这包括函数、变量、参数、结构体字段、模块、crate、常量、宏、静态值、属性、类型、trait 或生命周期
下面的列表包含 Rust 中正在使用或者将来会用到的关键字。因此,这些关键字不能被用作标识符(除了 “[原始标识符][raw-identifiers]” 部分介绍的原始标识符这包括函数、变量、参数、结构体字段、模块、crate、常量、宏、静态值、属性、类型、trait 或生命周期
的名字。
[raw-identifiers]: #原始标识符
### 目前正在使用的关键字
如下关键字目前有对应其描述的功能
* `as` - 强制类型转换,消除特定包含项的 trait 的歧义,或者对 `use` 语句中的项重命名
* `async` - 返回一个 `Future` 而不是阻塞当前线程
* `await` - 暂停执行直到 `Future` 的结果就绪
* `break` - 立刻退出循环
* `const` - 定义常量或不变裸指针constant raw pointer
* `continue` - 继续进入下一次循环迭代
* `crate` - 在模块路径中,代指 crate root
* `dyn` - 动态分发 trait 对象
* `else` - 作为 `if``if let` 控制流结构的 fallback
* `enum` - 定义一个枚举
* `extern` - 链接一个外部函数或变量
* `false` - 布尔字面值 `false`
* `fn` - 定义一个函数或 **函数指针类型** (*function pointer type*)
* `for` - 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期
* `if` - 基于条件表达式的结果分支
* `impl` - 实现自有或 trait 功能
* `in` - `for` 循环语法的一部分
* `let` - 绑定一个变量
* `loop` - 无条件循环
* `match` - 模式匹配
* `mod` - 定义一个模块
* `move` - 使闭包获取其所捕获项的所有权
* `mut` - 表示引用、裸指针或模式绑定的可变性
* `pub` - 表示结构体字段、`impl` 块或模块的公有可见性
* `ref` - 通过引用绑定
* `return` - 从函数中返回
* `Self` - 定义或实现 trait 的类型的类型别名
* `self` - 表示方法本身或当前模块
* `static` - 表示全局变量或在整个程序执行期间保持其生命周期
* `struct` - 定义一个结构体
* `super` - 表示当前模块的父模块
* `trait` - 定义一个 trait
* `true` - 布尔字面值 `true`
* `type` - 定义一个类型别名或关联类型
* `union` - 定义一个 [union] 并且是 union 声明中唯一用到的关键字
* `unsafe` - 表示不安全的代码、函数、trait 或实现
* `use` - 引入外部空间的符号
* `where` - 表示一个约束类型的从句
* `while` - 基于一个表达式的结果判断是否进行循环
如下为目前正在使用的关键字及其功能描述的列表
- `as` - 原始类型转换,消除特定包含项的 trait 的歧义,或者对 `use` 语句中的项重命名
- `async` - 返回一个 `Future` 而不是阻塞当前线程
- `await` - 暂停执行直到 `Future` 的结果就绪
- `break` - 立刻退出循环
- `const` - 定义常量或常量裸指针constant raw pointer
- `continue` - 继续进入下一次循环迭代
- `crate` - 在模块路径中,代指 crate root
- `dyn` - 动态分发 trait 对象
- `else` - 作为 `if``if let` 控制流结构的 fallback
- `enum` - 定义一个枚举
- `extern` - 链接一个外部函数或变量
- `false` - 布尔字面值 `false`
- `fn` - 定义一个函数或 **函数指针类型** (*function pointer type*)
- `for` - 遍历一个迭代器或实现一个 trait 或者指定一个更高级的生命周期
- `if` - 基于条件表达式的结果分支
- `impl` - 实现自有或 trait 功能
- `in` - `for` 循环语法的一部分
- `let` - 绑定一个变量
- `loop` - 无条件循环
- `match` - 模式匹配
- `mod` - 定义一个模块
- `move` - 使闭包获取其所捕获项的所有权
- `mut` - 表示引用、裸指针或模式绑定的可变性
- `pub` - 表示结构体字段、`impl` 块或模块的公有可见性
- `ref` - 通过引用绑定
- `return` - 从函数中返回
- `Self` - 定义或实现 trait 的类型的类型别名
- `self` - 表示方法本身或当前模块
- `static` - 表示全局变量或在整个程序执行期间保持其生命周期
- `struct` - 定义一个结构体
- `super` - 表示当前模块的父模块
- `trait` - 定义一个 trait
- `true` - 布尔字面值 `true`
- `type` - 定义一个类型别名或关联类型
- `union` - 定义一个 [union];仅在 union 声明中作为关键字
- `unsafe` - 表示不安全的代码、函数、trait 或实现
- `use` - 将符号引入作用域;为泛型和生命周期约束指定精确捕获
- `where` - 表示一个约束类型的从句
- `while` - 根据表达式的结果进行条件循环
[union]: https://doc.rust-lang.org/reference/items/unions.html
### 保留做将来使用的关键字
如下关键字没有任何功能,不过由 Rust 保留以备将来的应用。
* `abstract`
* `become`
* `box`
* `do`
* `final`
* `macro`
* `override`
* `priv`
* `try`
* `typeof`
* `unsized`
* `virtual`
* `yield`
### 为将来使用保留的关键字
以下关键字目前尚无任何功能,但已被 Rust 保留以备将来使用。
- `abstract`
- `become`
- `box`
- `do`
- `final`
- `gen`
- `macro`
- `override`
- `priv`
- `try`
- `typeof`
- `unsized`
- `virtual`
- `yield`
### 原始标识符
原始标识符Raw identifiers允许你使用通常不能使用的关键字其带有 `r#` 前缀
**原始标识符**_Raw identifiers_是一种允许你使用通常不能使用的关键字的语法。通过在关键字前加上 `r#` 前缀来使用原始标识符
例如,`match` 是关键字。如果尝试编译如下使用 `match` 作为名字的函数:
@ -95,7 +95,7 @@ error: expected identifier, found keyword `match`
| ^^^^^ expected identifier, found keyword
```
该错误表示你不能将关键字 `match` 用作函数标识符。你可以使用原始标识符将 `match` 作为函数名称使用
该错误表示你不能将关键字 `match` 用作函数标识符。要将 `match` 用作函数名称,需要使用原始标识符语法,如下所示
<span class="filename">文件名src/main.rs</span>
@ -111,6 +111,6 @@ fn main() {
此代码编译没有任何错误。注意 `r#` 前缀需同时用于函数名定义和 `main` 函数中的调用。
原始标识符允许使用你选择的任何单词作为标识符,即使该单词恰好是保留关键字。这给予了我们更大的自由来选择名字,这样与其他语言交互式就不用考虑到关键字问题,在要交互的语言中这个名字不是关键字。此外,原始标识符允许你使用以不同于你的 crate 使用的 Rust 版本编写的库。比如,`try` 在 2015 edition 中不是关键字,而在 2018 edition 则是。所以如果用 2015 edition 编写的库中带有 `try` 函数,在 2018 edition 中调用时就需要使用原始标识符语法,在这里是 `r#try`。有关版本的更多信息,请参见[附录 E][appendix-e]。
原始标识符允许使用你选择的任何单词作为标识符,即使该单词恰好是保留关键字。这给予了我们更大的自由来选择名字,这样与其他语言交互式就不用考虑到关键字问题,在要交互的语言中这个名字不是关键字。此外,原始标识符允许你使用以不同于你的 crate 使用的 Rust 版本编写的库。比如,`try` 在 2015 edition 中不是关键字,而在 2018、2021 和 2024 editio 则是。所以如果用 2015 edition 编写的库中带有 `try` 函数,在 2018 edition 中调用时就需要使用原始标识符语法,在这里是 `r#try`。有关版本的更多信息,请参见[附录 E][appendix-e]。
[appendix-e]: appendix-05-editions.html

@ -1,10 +1,9 @@
## 附录 B运算符与符号
> [appendix-02-operators.md](https://github.com/rust-lang/book/blob/main/src/appendix-02-operators.md)
> <br />
> commit 396fdb69de7fb18f24b15c7ad13491b1c1fa7231
<!-- https://github.com/rust-lang/book/blob/main/src/appendix-02-operators.md -->
<!-- commit 0833386eab8f37c60b8d80e05fabc4a97d0628ee -->
该附录包含了 Rust 语法的词汇表,包括运算符以及其他的符号这些符号单独出现或出现在路径、泛型、trait bounds、宏、属性、注释、元组以及大括号上下文中。
该附录包含了 Rust 语法的词汇表,包括运算符以及其符号这些符号单独出现或出现在路径、泛型、trait bounds、宏、属性、注释、元组以及大括号上下文中。
### 运算符
@ -35,10 +34,12 @@
| `-` | `- expr` | 算术取负 | `Neg` |
| `-` | `expr - expr` | 算术减法 | `Sub` |
| `-=` | `var -= expr` | 算术减法与赋值 | `SubAssign` |
| `->` | `fn(...) -> type`, <code>&vert;...&vert; -> type</code> | 函数与闭包,返回类型 | |
| `.` | `expr.ident` | 成员访问 | |
| `..` | `..`, `expr..`, `..expr`, `expr..expr` | 右开区间范围 | `PartialOrd` |
| `..=` | `..=expr`, `expr..=expr` | 右闭区间范围模式 | `PartialOrd` |
| `->` | `fn(...) -> type`, <code>&vert;...&vert; -> type</code> | 函数与闭包的返回类型 | |
| `.` | `expr.ident` | 字段访问 | |
| `.` | `expr.ident(expr, ...)` | 方法调用 | |
| `.` | `expr.0`, `expr.1`, etc. | 元组索引 | |
| `..` | `..`, `expr..`, `..expr`, `expr..expr` | 右开区间范围字面值 | `PartialOrd` |
| `..=` | `..=expr`, `expr..=expr` | 右闭区间范围字面值 | `PartialOrd` |
| `..` | `..expr` | 结构体更新语法 | |
| `..` | `variant(x, ..)`, `struct_type { x, .. }` | “与剩余部分” 的模式绑定 | |
| `...` | `expr...expr` | Deprecated请使用 `..=`)在模式中:闭区间范围模式 | |
@ -55,15 +56,15 @@
| `<=` | `expr <= expr` | 小于等于比较 | `PartialOrd` |
| `=` | `var = expr`, `ident = type` | 赋值/等值 | |
| `==` | `expr == expr` | 等于比较 | `PartialEq` |
| `=>` | `pat => expr` | 匹配准备语法的部分 | |
| `=>` | `pat => expr` | 匹配分支语法的部分 | |
| `>` | `expr > expr` | 大于比较 | `PartialOrd` |
| `>=` | `expr >= expr` | 大于等于比较 | `PartialOrd` |
| `>=` | `expr >= expr` | 大于等于比较 | `PartialOrd` |
| `>>` | `expr >> expr` | 右移 | `Shr` |
| `>>=` | `var >>= expr` | 右移与赋值 | `ShrAssign` |
| `@` | `ident @ pat` | 模式绑定 | |
| `^` | `expr ^ expr` | 按位异或 | `BitXor` |
| `^=` | `var ^= expr` | 按位异或与赋值 | `BitXorAssign` |
| <code>&vert;</code> | <code>pat &vert; pat</code> | 模式选择 | |
| <code>&vert;</code> | <code>pat &vert; pat</code> | 模式替代 | |
| <code>&vert;</code> | <code>expr &vert; expr</code> | 按位或 | `BitOr` |
| <code>&vert;=</code> | <code>var &vert;= expr</code> | 按位或与赋值 | `BitOrAssign` |
| <code>&vert;&vert;</code> | <code>expr &vert;&vert; expr</code> | 短路Short-circuiting逻辑或 | |
@ -71,7 +72,7 @@
### 非运算符符号
下面的列表中包含了所有和运算符不一样功能的符号;也就是说,它们并不像函数调用或方法调用一样表现
下面的列表中包含了所有和运算符不一样功能的符号;也就是说,它们不表现为函数或方法调用
表 B-2 展示了以其自身出现以及出现在合法其他各个地方的符号。
@ -81,7 +82,7 @@
|--------|-------------|
| `'ident` | 命名生命周期或循环标签 |
| `...u8`, `...i32`, `...f64`, `...usize` 等 | 指定类型的数值常量 |
| `"..."` | 字符串常量 |
| `"..."` | 字符串字面值 |
| `r"..."`, `r#"..."#`, `r##"..."##`, etc. | 原始字符串字面值,未处理的转义字符 |
| `b"..."` | 字节字符串字面值; 构造一个字节数组类型而非字符串 |
| `br"..."`, `br#"..."#`, `br##"..."##` 等 | 原始字节字符串字面值,原始和字节字符串字面值的结合 |
@ -98,9 +99,9 @@
| 符号 | 解释 |
|--------|-------------|
| `ident::ident` | 命名空间路径 |
| `::path` | 与 crate 根相对的路径(如一个显式绝对路径) |
| `self::path` | 与当前模块相对的路径(一个显式相对路径)|
| `super::path` | 与父模块相对的路径 |
| `::path` | 与 extern prelude 相对的路径,其他所有 crate 都以此为根(即一个包含 crate 名称的显式绝对路径) |
| `self::path` | 与当前模块相对的路径(一个显式相对路径)|
| `super::path` | 与当前模块的父模块相对的路径 |
| `type::ident`, `<type as trait>::ident` | 关联常量、函数以及类型 |
| `<type>::...` | 不可以被直接命名的关联项类型(如 `<&T>::...``<[T]>::...`,等) |
| `trait::method(...)` | 通过命名定义的 trait 来消除方法调用的二义性 |
@ -108,7 +109,7 @@
| `<type as trait>::method(...)` | 通过命名 trait 和类型来消除方法调用的二义性 |
表 B-4 展示了出现在泛型类型参数上下文中的符号。
表 B-4 展示了用于泛型类型参数上下文中的符号。
<span class="caption">表 B-4泛型</span>
@ -123,7 +124,7 @@
| `for<...> type` | 高级生命周期限制 |
| `type<ident=type>` | 泛型,其一个或多个相关类型必须被指定为特定类型(如 `Iterator<Item=T>`|
表 B-5 展示了出现在使用 trait bounds 约束泛型参数上下文中的符号。
表 B-5 展示了出现在使用 trait bound 约束泛型参数上下文中的符号。
<span class="caption">表 B-5: Trait Bound 约束</span>
@ -131,12 +132,12 @@
|--------|-------------|
| `T: U` | 泛型参数 `T` 约束于实现了 `U` 的类型 |
| `T: 'a` | 泛型 `T` 的生命周期必须长于 `'a`(意味着该类型不能传递包含生命周期短于 `'a` 的任何引用)|
| `T: 'static` | 泛型 T 不包含除 'static 之外的借用引用 |
| `T: 'static` | 泛型 T 不包含除 `'static` 之外的借用引用 |
| `'b: 'a` | 泛型 `'b` 生命周期必须长于泛型 `'a` |
| `T: ?Sized` | 使用一个不定大小的泛型类型 |
| `'a + trait`, `trait + trait` | 复合类型限制 |
表 B-6 展示了在调用或定义宏以及在其上指定属性时的上下文中出现的符号。
表 B-6 展示了在调用或定义宏以及在项上指定属性的上下文中出现的符号。
<span class="caption">表 B-6: 宏与属性</span>
@ -149,7 +150,7 @@
| `$(…)…` | 宏重复 |
| `ident!(...)`, `ident!{...}`, `ident![...]` | 宏调用 |
表 B-7 展示了注释的符号。
表 B-7 展示了创建注释的符号。
<span class="caption">表 B-7: 注释</span>
@ -162,9 +163,9 @@
| `/*!...*/` | 内部块文档注释 |
| `/**...*/` | 外部块文档注释 |
表 B-8 展示了出现在使用元组时上下文中的符号。
表 B-8 展示了出现在使用圆括号上下文中的符号。
<span class="caption">表 B-8: 元组</span>
<span class="caption">表 B-8: 圆括号</span>
| 符号 | 解释 |
|--------|-------------|
@ -175,7 +176,6 @@
| `(expr, ...)` | 元组表达式 |
| `(type, ...)` | 元组类型 |
| `expr(expr, ...)` | 函数调用表达式;也用于初始化元组结构体 `struct` 以及元组枚举 `enum` 变体 |
| `expr.0`, `expr.1`, etc. | 元组索引 |
表 B-9 展示了使用大括号的上下文。
@ -192,8 +192,8 @@
| 符号 | 解释 |
|---------|-------------|
| `[...]` | 数组 |
| `[expr; len]` | 复制了 `len``expr`的数组 |
| `[type; len]` | 包含 `len``type` 类型的数组|
| `[...]` | 数组字面值 |
| `[expr; len]` | 复制了 `len` `expr` 的数组 |
| `[type; len]` | 包含 `len` `type` 类型的数组|
| `expr[expr]` | 集合索引。重载(`Index`, `IndexMut` |
| `expr[..]`, `expr[a..]`, `expr[..b]`, `expr[a..b]` | 集合索引,使用 `Range``RangeFrom``RangeTo` 或 `RangeFull` 作为索引来代替集合 slice |

@ -1,8 +1,7 @@
## 附录 C可派生的 trait
> [appendix-03-derivable-traits.md](https://github.com/rust-lang/book/blob/main/src/appendix-03-derivable-traits.md)
> <br />
> commit c07dddac692848ade6c2112c8e15a7087fbbec45
<!-- https://github.com/rust-lang/book/blob/main/src/appendix-03-derivable-traits.md -->
<!-- commit 56ec353290429e6547109e88afea4de027b0f1a9 -->
在本书的各个部分中,我们讨论了可应用于结构体和枚举定义的 `derive` 属性。`derive` 属性会在使用 `derive` 语法标记的类型上生成对应 trait 的默认实现的代码。
@ -10,17 +9,17 @@
- 该 trait 将会派生什么样的操作符和方法
- 由 `derive` 提供什么样的 trait 实现
- 由什么来实现类型的 trait
- 是否允许实现该 trait 的条件
- 实现该 trait 对类型意味着什么
- 在何种条件下允许或不允许实现该 trait
- 需要 trait 操作的例子
如果你希望不同于 `derive` 属性所提供的行为,请查阅 [标准库文档](https://doc.rust-lang.org/std/index.html) 中每个 trait 的细节以了解如何手动实现它们。
如果你希望不同于 `derive` 属性所提供的行为,请查阅[标准库文档](https://doc.rust-lang.org/std/index.html) 中每个 trait 的细节以了解如何手动实现它们。
这里列出的 trait 是仅有的在标准库中定义且能通过 `derive` 在类型上实现。标准库中定义的其它 trait 不能通过 `derive` 在类型上实现。这些 trait 不存在有意义的默认行为,所以由你负责以合理的方式实现它们。
一个无法被派生的 trait 的例子是为终端用户处理格式化的 `Display` 。你应该时常考虑使用合适的方法来为终端用户显示一个类型。终端用户应该看到类型的什么部分他们会找出相关部分吗对他们来说最相关的数据格式是什么样的Rust 编译器没有这样的洞察力,因此无法为你提供合适的默认行为。
本附录所提供的可派生 trait 列表并不全面:库可以为其自己的 trait 实现 `derive`,可以使用 `derive` 的 trait 列表事实上是无限的。实现 `derive` 涉及到过程宏的应用,这在第二十章的 [“宏”][macros] 有介绍。
本附录所提供的可派生 trait 列表并不全面:库可以为其自己的 trait 实现 `derive`,可以使用 `derive` 的 trait 列表事实上是无限的。实现 `derive` 涉及到过程宏的应用,这在第二十章的 [“宏”][macros] 一节中有介绍。
### 用于程序员输出的 `Debug`
@ -28,49 +27,49 @@
`Debug` trait 允许以调试目的来打印一个类型的实例,所以使用该类型的程序员可以在程序执行的特定时间点观察其实例。
例如,在使用 `assert_eq!` 宏时,`Debug` trait 是必的。如果等式断言失败,这个宏就把给定实例的值作为参数打印出来,如此程序员可以看到两个实例为什么不相等。
例如,在使用 `assert_eq!` 宏时,`Debug` trait 是必的。如果等式断言失败,这个宏就把给定实例的值作为参数打印出来,如此程序员可以看到两个实例为什么不相等。
### 等值比较的 `PartialEq``Eq`
`PartialEq` trait 可以比较个类型的实例以检查是否相等,并开启了 `==``!=` 运算符的功能。
`PartialEq` trait 可以比较个类型的实例以检查是否相等,并开启了 `==``!=` 运算符的功能。
派生的 `PartialEq` 实现了 `eq` 方法。当 `PartialEq` 在结构体上派生时,只有*所有* 的字段都相等时两个实例才相等,同时只要有任何字段不相等则两个实例就不相等。当在枚举上派生时,每一个成员都和其自身相等,且和其他成员都不相等。
派生的 `PartialEq` 实现了 `eq` 方法。当 `PartialEq` 在结构体上派生时,只有**所有**的字段都相等时两个实例才相等,同时只要有任何字段不相等则两个实例就不相等。当在枚举上派生时,每一个变体都和其自身相等,且和其它变体都不相等。
例如,当使用 `assert_eq!` 宏时,需要比较一个类型的两个实例是否相等,则 `PartialEq` trait 是必须的。
`Eq` trait 没有方法。其作用是表明每一个被标记类型的值等于其自身。`Eq` trait 只能应用于那些实现了 `PartialEq` 的类型,但并非所有实现了 `PartialEq` 的类型都可以实现 `Eq`。浮点类型就是一个例子:浮点数的实现表明两个非数字(`NaN`not-a-number值是互不相等的。
例如,对于一个 `HashMap<K, V>` 中的 key 来说, `Eq` 是必须的,这样 `HashMap<K, V>` 就可以知道两个 key 是否一样了。
例如,对于一个 `HashMap<K, V>` 中的key来说`Eq` 是必须的,这样 `HashMap<K, V>` 就可以知道两个键是否相等了。
### 序比较的 `PartialOrd``Ord`
### 序比较的 `PartialOrd``Ord`
`PartialOrd` trait 可以基于排序的目的而比较一个类型的实例。实现了 `PartialOrd` 的类型可以使用 `<``>`、`<=` 和 `>=` 操作符。但只能在同时实现了 `PartialEq` 的类型上使用 `PartialOrd`
派生 `PartialOrd` 实现了 `partial_cmp` 方法,其返回一个 `Option<Ordering>` ,但当给定值无法产生顺序时将返回 `None`。尽管大多数类型的值都可以比较,但一个无法产生顺序的例子是:浮点类型的非数字值。当在浮点数上调用 `partial_cmp` 时,`NaN` 的浮点数将返回 `None`
派生 `PartialOrd` 实现了 `partial_cmp` 方法,其返回一个 `Option<Ordering>`,但当给定值无法产生顺序时将返回 `None`。尽管大多数类型的值都可以比较,但一个无法产生顺序的例子是:浮点类型的非数字值not-a-number`NaN`)。对任何浮点数与 `NaN` 调用 `partial_cmp` 都会返回 `None`
当在结构体上派生时,`PartialOrd` 按照结构体定义中字段出现的顺序,依次比较每个字段的值,以此来比较两个实例。当在枚举上派生时,认为在枚举定义中声明较早的枚举变体小于其后的变体。
例如,对于来自于 `rand` crate 中的 `gen_range` 方法来说,当在一个范围表达式指定的范围内生成一个随机值时,`PartialOrd` trait 是必须的。
`Ord` trait 也让你明白在一个带注解类型上的任意两个值存在有效顺序。`Ord` trait 实现了 `cmp` 方法,它返回一个 `Ordering` 而不是 `Option<Ordering>`,因为总存在一个合法的顺序。只可以在实现了 `PartialOrd``Eq``Eq` 依赖 `PartialEq`)的类型上使用 `Ord` trait。当在结构体或枚举上派生时 `cmp` 和以 `PartialOrd` 派生实现的 `partial_cmp` 表现一致
`Ord` trait 也让你知道在一个带注解类型上的任意两个值存在有效顺序。`Ord` trait 实现了 `cmp` 方法,它返回一个 `Ordering` 而不是 `Option<Ordering>`,因为总存在一个合法的顺序。只可以在实现了 `PartialOrd``Eq``Eq` 依赖 `PartialEq`)的类型上使用 `Ord` trait。当在结构体或枚举上派生时`cmp` 的行为与 `PartialOrd` 派生实现的 `partial_cmp` 相同
例如,当在 `BTreeSet<T>`(一种基于有序值存储数据的数据结构)上存值时,`Ord` 是必须的
例如,将值存储到 `BTreeSet<T>` 中时,需要 `Ord` trait因为该数据结构基于值的排序顺序来存储数据
### 复制值的 `Clone``Copy`
`Clone` trait 可以明确地创建一个值的深拷贝deep copy复制过程可能包含任意代码的执行以及堆上数据的复制。查阅第四章 [“使用克隆的变量与数据交互”][ways-variables-and-data-interact-clone] 以获取有关 `Clone` 的更多信息。
`Clone` trait 可以明确地创建一个值的深拷贝deep copy复制过程可能包含任意代码的执行以及堆上数据的复制。查阅第四章 [“使用克隆的变量与数据交互”][variables-and-data-interacting-with-clone] 以获取有关 `Clone` 的更多信息。
派生 `Clone` 实现了 `clone` 方法,其为整个类型实现时,在类型的每一部分上调用了 `clone` 方法。这意味着类型中所有字段或值也必须实现了 `Clone`,这样才能够派生 `Clone`
派生 `Clone` 实现了 `clone` 方法,其为整个类型实现时,在类型的每一部分上调用了 `clone` 方法。这意味着类型中所有字段或值也必须实现了 `Clone`,这样才能够派生 `Clone`
例如,当在一个切片slice上调用 `to_vec` 方法时,`Clone` 是必须的。切片并不拥有其包含的实例,但是从 `to_vec` 中返回的 vector 需要拥有其实例,因此,`to_vec` 在每个元素上调用 `clone`。因此,存储在切片中的类型必须实现 `Clone`
例如,当在一个 slice 上调用 `to_vec` 方法时,`Clone` 是必须的。slice 并不拥有其包含的实例,但是从 `to_vec` 中返回的 vector 需要拥有它们的实例,因此 `to_vec` 在每个元素上调用 `clone`。所以存储在切片中的类型必须实现 `Clone`
`Copy` trait 允许你通过只拷贝存储在栈上的位来复制值而不需要额外的代码。查阅第四章 [“只在栈上的数据:拷贝”][stack-only-data-copy] 的部分来获取有关 `Copy` 的更多信息。
`Copy` trait 允许你通过只拷贝存储在栈上的位来复制值;无需执行额外的代码。查阅第四章 [“只在栈上的数据:拷贝”][stack-only-data-copy] 的部分来获取有关 `Copy` 的更多信息。
`Copy` trait 并未定义任何方法来阻止编程人员重写这些方法或违反不需要执行额外代码的假设。尽管如此所有的编程人员可以假设复制copy一个值非常快
`Copy` trait 并未定义任何方法来阻止编程人员重写这些方法或违反无需执行额外代码的假设。这样,所有程序员都可以假定复制值会非常快速
可以在类型内部全部实现 `Copy` trait 的任意类型上派生 `Copy`。一个实现了 `Copy` 的类型必须也实现了 `Clone`,因为一个实现了 `Copy` 的类型也简单地实现了 `Clone`,其执行和 `Copy` 相同的任务。
`Copy` trait 很少使用;实现 `Copy` 的类型是可以优化的,这意味着你无需调用 `clone`,这让代码更简洁。
`Copy` trait 很少是必需的;实现 `Copy` 的类型是有优化的,这意味着你无需调用 `clone`,这让代码更简洁。
任何使用 `Copy` 的代码都可以通过 `Clone` 实现,但代码可能会稍慢,或者不得不在代码中的许多位置上使用 `clone`
@ -80,15 +79,17 @@
例如,在 `HashMap<K, V>` 上存储数据,存放 key 的时候,`Hash` 是必须的。
一个 `Hash` 是必须的例子是在 `HashMap<K, V>` 中存储键来高效地存储数据。
### 默认值的 `Default`
`Default` trait 使你创建一个类型的默认值。派生 `Default` 实现了 `default` 函数。`default` 函数的派生实现调用了类型每部分的 `default` 函数,这意味着类型中所有的字段或值也必须实现了 `Default`,这样才能够派生 `Default`
`Default::default` 函数通常结合结构体更新语法一起使用,这在第五章的 [“使用结构体更新语法从其他实例中创建实例”][creating-instances-from-other-instances-with-struct-update-syntax] 部分有讨论。可以自定义一个结构体的一小部分字段而剩余字段则使用 `..Default::default()` 设置默认值。
`Default::default` 函数通常结合结构体更新语法一起使用,这在第五章的 [“使用结构体更新语法从其他实例中创建实例”][creating-instances-from-other-instances-with-struct-update-syntax] 部分有讨论。可以自定义一个结构体的一小部分字段而剩余字段则使用 `..Default::default()` 设置默认值。
例如,当你在 `Option<T>` 实例上使用 `unwrap_or_default` 方法时,`Default` trait 是必须的。如果 `Option<T>``None`的话,`unwrap_or_default` 方法将返回存储在 `Option<T>``T` 类型的 `Default::default` 的结果。
例如,当你在 `Option<T>` 实例上使用 `unwrap_or_default` 方法时,`Default` trait 是必须的。如果 `Option<T>``None` 的话,`unwrap_or_default` 方法将返回存储在 `Option<T>``T` 类型的 `Default::default` 的结果。
[creating-instances-from-other-instances-with-struct-update-syntax]: ch05-01-defining-structs.html#使用结构体更新语法从其他实例创建实例
[stack-only-data-copy]: ch04-01-what-is-ownership.html#只在栈上的数据拷贝
[ways-variables-and-data-interact-clone]: ch04-01-what-is-ownership.html#使用克隆的变量与数据交互
[variables-and-data-interacting-with-clone]: ch04-01-what-is-ownership.html#使用克隆的变量与数据交互
[macros]: ch20-05-macros.html#宏

@ -1,64 +1,66 @@
## 附录 D实用开发工具
> [appendix-04-useful-development-tools.md](https://github.com/rust-lang/book/blob/main/src/appendix-04-useful-development-tools.md)
> <br />
> commit 5057f157cd0b35bc7d0dc0af6ef622fa4c480996
<!-- https://github.com/rust-lang/book/blob/main/src/appendix-04-useful-development-tools.md -->
<!-- commit 56ec353290429e6547109e88afea4de027b0f1a9 -->
本附录,我们将讨论 Rust 项目提供的用于开发 Rust 代码的工具
在本附录中,我们将讨论 Rust 项目提供的一些有助于开发 Rust 代码的工具。我们将介绍自动格式化、快速应用警告修复、linter 以及与 IDE 的集成
### 通过 `rustfmt` 自动格式化
`rustfmt` 工具根据社区代码风格格式化代码。很多项目使用 `rustfmt` 来避免编写 Rust 风格的争论:所有人都用这个工具格式化代码!
`rustfmt` 工具根据社区代码风格格式化代码。很多项目使用 `rustfmt` 来避免编写 Rust 代码风格的争论:所有人都用这个工具格式化代码!
安装 `rustfmt`
```console
$ rustup component add rustfmt
Rust 安装默认已包含 rustfmt因此你的系统上应该已经有 `rustfmt``cargo-fmt` 程序了。这两个命令类似于 `rustc``cargo`,其中 `rustfmt` 提供了更细粒度的控制,而 `cargo-fmt` 则理解使用 Cargo 的项目约定。要格式化任何 Cargo 项目,请输入以下命令:
```sh
$ cargo fmt
```
这会提供 `rustfmt``cargo-fmt`,类似于 Rust 同时安装 `rustc``cargo`。为了格式化整个 Cargo 项目:
运行此命令会格式化当前 crate 中所有的 Rust 代码。这应该只会改变代码风格,而不是代码语义。
该命令会为你提供 `rustfmt``cargo-fmt`,类似于 Rust 同时提供 `rustc``cargo`。要格式化任何 Cargo 项目,请执行以下命令:
```console
$ cargo fmt
```
运行此命令会格式化当前 crate 中所有的 Rust 代码。这应该只会改变代码风格,而不是代码语义。请查看 [该文档][rustfmt] 了解 `rustfmt` 的更多信息
运行此命令会格式化当前 crate 中所有的 Rust 代码。这应该只会改变代码风格,而不是代码语义。有关 `rustfmt` 的更多信息,请参阅 [该文档][rustfmt]
[rustfmt]: https://github.com/rust-lang/rustfmt
### 通过 `rustfix` 修复代码
如果你编写过 Rust 代码,那么你可能见过那些有很明显修复方式的编译器警告。例如,考虑如下代码:
`rustfix` 工具已随 Rust 安装一并提供,可以自动修复那些具有明确修复方式的编译器警告,这通常正是你所需要的。你可能已经见过类似的编译器警告。例如,考虑如下代码:
<span class="filename">文件名src/main.rs</span>
```rust
fn do_something() {}
fn main() {
for i in 0..100 {
do_something();
}
let mut x = 42;
println!("{x}");
}
```
这里定义变量 `x` 为可变但是我们从未修改它。Rust 会警告说:
这里调用了 `do_something` 函数 100 次,不过从未在 `for` 循环体中使用变量 `i`。Rust 会警告说:
```console
$ cargo build
Compiling myprogram v0.1.0 (file:///projects/myprogram)
warning: unused variable: `i`
--> src/main.rs:4:9
warning: variable does not need to be mutable
--> src/main.rs:2:9
|
4 | for i in 0..100 {
| ^ help: consider using `_i` instead
2 | let mut x = 0;
| ----^
| |
| help: remove this `mut`
|
= note: #[warn(unused_variables)] on by default
Finished dev [unoptimized + debuginfo] target(s) in 0.50s
= note: `#[warn(unused_mut)]` on by default
```
警告中建议使用 `_i` 名称:下划线表明该变量有意不使用。我们可以通过 `cargo fix` 命令使用 `rustfix` 工具来自动采用该建议:
警告中建议移除 `mut` 关键字。我们可以通过运行 `cargo fix` 命令使用 `rustfix` 工具来自动采用该建议:
```console
$ cargo fix
@ -72,36 +74,27 @@ $ cargo fix
<span class="filename">文件名src/main.rs</span>
```rust
fn do_something() {}
fn main() {
for _i in 0..100 {
do_something();
}
let x = 42;
println!("{x}");
}
```
现在 `for` 循环变量变为 `_i`,警告也不再出现。
`cargo fix` 命令可以用于在不同 Rust 版本间迁移代码。版本在附录 E 中介绍。
变量 `x` 现在是不可变的了,警告也不再出现。
### 通过 `clippy` 提供更多 lint 功能
`cargo fix` 命令可以用于在不同 Rust 版本间迁移代码。版本在[附录 E][editions]中介绍。
`clippy` 工具是一系列 lint 的集合,用于捕捉常见错误和改进 Rust 代码。
### 使用 Clippy 获取更多 lint
安装 `clippy`
```console
$ rustup component add clippy
```
Clippy 工具是一组 lints 的集合,用于分析你的代码,帮助你捕捉常见错误并改进 Rust 代码。Clippy 已包含在 Rust 的标准安装中。
对任何 Cargo 项目运行 clippy 的 lint
要对任何 Cargo 项目运行 Clippy 的 lint请输入以下命令
```console
$ cargo clippy
```
例如,如果程序使用了如 pi 这样数学常数的近似值,如下
例如,你编写了一个程序使用了数学常数,例如 pi的一个近似值如下所示
<span class="filename">文件名src/main.rs</span>
@ -127,7 +120,7 @@ error: approximate value of `f{32, 64}::consts::PI` found
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#approx_constant
```
这告诉我们 Rust 定义了更为精确的常量,而如果使用了这些常量程序将更加准确。如下代码就不会导致 `clippy` 产生任何错误或警告:
该错误提示你 Rust 已经定义了一个更精确的 `PI` 常量,如果使用该常量,你的程序会更为正确。你可以将代码改为使用 `PI` 常量。如下代码就不会引发 Clippy 的任何错误或警告:
<span class="filename">文件名src/main.rs</span>
@ -139,17 +132,18 @@ fn main() {
}
```
请查看 [其文档][clippy] 来了解 `clippy` 的更多信息
有关 Clippy 的更多信息,请参阅 [其文档][clippy]
[clippy]: https://github.com/rust-lang/rust-clippy
### 使用 `rust-analyzer` 的 IDE 集成
为了帮助 IDE 集成Rust 社区建议使用 [`rust-analyzer`][rust-analyzer]。这个工具是一组以编译器为中心的实用程序,它实现了 [Language Server Protocol][lsp](一个 IDE 与编程语言之间的通信规范)。`rust-analyzer` 可以用于不同的客户端,比如 [Visual Studio Code 的 Rust analyzer 插件][vscode]。
为了帮助 IDE 集成Rust 社区建议使用 [`rust-analyzer`][rust-analyzer]。这个工具是一组以编译器为中心的实用程序,它实现了 [Language Server Protocol][lsp],一个 IDE 与编程语言之间的通信规范。`rust-analyzer` 可以用于不同的客户端,比如 [Visual Studio Code 的 Rust analyzer 插件][vscode]。
[lsp]: http://langserver.org/
[vscode]: https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer
访问 `rust-analyzer` 项目的 [主页][rust-analyzer] 来了解如何安装它,然后为你的 IDE 安装 language server 支持。如此你的 IDE 便会获得如自动补全、跳转到定义和 inline error 之类的功能。
访问 `rust-analyzer` 项目的[主页][rust-analyzer]来了解如何安装它,然后为你的 IDE 安装 language server 支持。如此你的 IDE 便会获得如自动补全、跳转到定义和 inline error 之类的功能。
[rust-analyzer]: https://rust-analyzer.github.io
[editions]: appendix-05-editions.html

@ -1,22 +1,21 @@
## 附录 E版本
> [appendix-05-editions.md](https://github.com/rust-lang/book/blob/main/src/appendix-05-editions.md)
> <br />
> commit 8cf0496bb8e56b683ea3f015871c8631684decf4
<!-- https://github.com/rust-lang/book/blob/main/src/appendix-05-editions.md -->
<!-- commit 56ec353290429e6547109e88afea4de027b0f1a9 -->
早在第一章,我们见过 `cargo new`*Cargo.toml* 中增加了一些有关 `edition` 的元数据。本附录将解释其意义!
Rust 语言和编译器有一个为期 6 周的发布循环。这意味着用户会稳定得到新功能的更新。其他编程语言发布大更新但不甚频繁Rust 选择更为频繁的发布小更新。一段时间之后,所有这些小更新会日积月累。不过随着小更新逐次的发布,或许很难回过头来感叹:“哇,从 Rust 1.10 到 Rust 1.31Rust 的变化真大!”
Rust 语言和编译器有一个为期六周的发布循环,这意味着用户会稳定得到新功能的更新。其他编程语言发布大更新但不甚频繁Rust 选择更为频繁的发布小更新。一段时间之后,所有这些小更新会日积月累。不过随着小更新逐次的发布,或许很难回过头来感叹:“哇,从 Rust 1.10 到 Rust 1.31Rust 的变化真大!”
每两到三年Rust 团队会生成一个新的 Rust **版本***edition*)。每一个版本会结合已经落地的功能,并提供一个清晰的带有完整更新文档和工具的功能包。新版本会作为常规的 6 周发布过程的一部分发布。
大约每两到三年Rust 团队会生成一个新的 Rust **版本***edition*)。每一个版本会结合已经落地的功能,并提供一个清晰的带有完整更新文档和工具的功能包。新版本会作为常规的周发布过程的一部分发布。
这为不同的人群提供了不同的功能
新的版本对不同人群具有不同意义
- 对于活跃的 Rust 用户,其将增量的修改与易于理解的功能包相结合
- 对于非用户,它表明发布了一些重大进展,这意味着 Rust 可能变得值得一试。
- 对于 Rust 自身开发者,提供了项目整体的集合点。
- 对于活跃的 Rust 用户,新版本将这些增量改进整合成一个易于理解的包
- 对于非 Rust 用户,它表明发布了一些重大进展,这意味着 Rust 可能变得值得一试。
- 对于 Rust 自身开发者,提供了项目整体的集合点。
在本文档编写时Rust 有三个可用版本Rust 2015、Rust 2018 和 Rust 2021。本书基于 Rust 2021 edition 风格编写。
在本文档编写时Rust 有四个可用版本Rust 2015、Rust 2018、Rust 2021 和 Rust 2024。本书基于 Rust 2021 edition 惯用法编写。
*Cargo.toml* 中的 `edition` 字段表明代码应该使用哪个版本编译。如果该字段不存在,其默认为 `2015` 以提供后向兼容性。
@ -26,4 +25,4 @@ Rust 语言和编译器有一个为期 6 周的发布循环。这意味着用户
有一点需要明确:大部分功能在所有版本中都能使用。开发者使用任何 Rust 版本将能继续接收最新稳定版的改进。然而在一些情况,主要是增加了新关键字的时候,则可能出现了只能用于新版本的功能。只需切换版本即可利用新版本的功能。
请查看 [Edition Guide](https://rust-lang-nursery.github.io/edition-guide/) 了解更多细节,这是一个全介绍版本的书籍,包括如何通过 `cargo fix` 自动将代码迁移到新版本。
请查看 [_Edition Guide_](https://rust-lang-nursery.github.io/edition-guide/) 了解更多细节,这是一个全介绍不同版本之间差异的书籍,包括如何通过 `cargo fix` 自动将代码迁移到新版本。

@ -1,22 +1,20 @@
## 附录 F本书译本
> [appendix-06-translation.md](https://github.com/rust-lang/book/blob/main/src/appendix-06-translation.md)
> <br />
> commit 4c8d13c52c51f1c62a80b52d7fbd7cc0b63ada43
<!-- https://github.com/rust-lang/book/blob/main/src/appendix-06-translation.md -->
<!-- commit fd0c2e900711a9c9e4b0b440f9156d926d52d513 -->
一些非英语语言的资源。多数仍在翻译中;查阅 [翻译标签][label] 来帮助翻译,或者添加译本链接!
一些非英语语言的资源。多数仍在翻译中;查阅[翻译标签][label]来帮助翻译,或者添加译本链接!
[label]: https://github.com/rust-lang/book/issues?q=is%3Aopen+is%3Aissue+label%3ATranslations
- [Português](https://github.com/rust-br/rust-book-pt-br) (BR)
- [Português](https://github.com/nunojesus/rust-book-pt-pt) (PT)
- [简体中文](https://github.com/KaiserY/trpl-zh-cn)
- 简体中文:[KaiserY/trpl-zh-cn](https://github.com/KaiserY/trpl-zh-cn), [gnu4cn/rust-lang-Zh_CN](https://github.com/gnu4cn/rust-lang-Zh_CN)
- [正體中文](https://github.com/rust-tw/book-tw)
- [Українська](https://github.com/pavloslav/rust-book-uk-ua)
- [Español](https://github.com/thecodix/book), [alternate](https://github.com/ManRR/rust-book-es)
- [Italiano](https://github.com/EmanueleGurini/book_it)
- [Українська](https://rust-lang-ua.github.io/rustbook_ukrainian)
- [Español](https://github.com/thecodix/book), [alternate](https://github.com/ManRR/rust-book-es), [Español por RustLangES](https://github.com/RustLangES/rust-book-es)
- [Русский](https://github.com/rust-lang-ru/book)
- [한국어](https://github.com/rinthel/rust-lang-book-ko)
- [한국어](https://github.com/rust-kr/doc.rust-kr.org)
- [日本語](https://github.com/rust-lang-ja/book-ja)
- [Français](https://github.com/Jimskapt/rust-book-fr)
- [Polski](https://github.com/paytchoo/book-pl)
@ -25,7 +23,7 @@
- [Esperanto](https://github.com/psychoslave/Rust-libro)
- [ελληνική](https://github.com/TChatzigiannakis/rust-book-greek)
- [Svenska](https://github.com/sebras/book)
- [Farsi](https://github.com/pomokhtari/rust-book-fa)
- [Farsi](https://github.com/RustFarsi/book), [Persian (FA)](https://github.com/persian-rust/book)
- [Deutsch](https://github.com/rust-lang-de/rustbook-de)
- [हिंदी](https://github.com/venkatarun95/rust-book-hindi)
- [ไทย](https://github.com/rust-lang-th/book-th)

@ -1,20 +1,19 @@
## 附录 GRust 是如何开发的与 “Nightly Rust”
> [appendix-07-nightly-rust.md](https://github.com/rust-lang/book/blob/main/src/appendix-07-nightly-rust.md)
> <br />
> commit d44317c3122b44fb713aba66cc295dee3453b24b
<!-- https://github.com/rust-lang/book/blob/main/src/appendix-07-nightly-rust.md -->
<!-- commit 3a30e4c1fbe641afc066b3af9eb01dcdf5ed8b24 -->
本附录介绍 Rust 是如何开发的以及这如何影响作为 Rust 开发者的你
本附录介绍 Rust 是如何开发的以及这对你作为 Rust 开发者的影响
### 无停滞稳定
作为一个语言Rust **十分** 注重代码的稳定性。我们希望 Rust 成为你代码坚实的基础,假如持续地有东西在变,这个希望就实现不了。但与此同时,如果不能实验新功能的话,在发布之前我们又无法发现其中重大的缺陷,而一旦发布便再也没有修改的机会了。
作为一门语言Rust **十分**注重代码的稳定性。我们希望 Rust 成为你可依赖的坚实基础,假如事务持续地在变化,这个希望就实现不了。但与此同时,如果不能实验新功能的话,在发布之前我们又无法发现其中重大的缺陷,而一旦发布便再也没有修改的机会了。
对于这个问题我们的解决方案被称为 “无停滞稳定”“stability without stagnation”其指导性原则是无需担心升级到最新的稳定版 Rust。每次升级应该是无痛的并应带来新功能更少的 bug 和更快的编译速度。
### Choo, Choo! 发布通道和发布时刻表Riding the Trains
Rust 开发运行于一个 **发布时刻表**_train schedule_之上。也就是说所有的开发工作都位于 Rust 仓库的 `master` 分支。发布采用 software release train 模型,其被用于思科 IOS 等其它软件项目。Rust 有三个 **发布通道**_release channel_
Rust 开发运行于一个**发布时刻表**_train schedule_之上。也就是说所有的开发工作都位于 Rust 仓库的 `master` 分支。发布采用 software release train 模型,其被用于思科 IOS 等其它软件项目。Rust 有三个**发布通道**_release channel_
- Nightly
- Beta
@ -22,13 +21,13 @@ Rust 开发运行于一个 **发布时刻表**_train schedule_之上。也
大部分 Rust 开发者主要采用稳定版通道,不过希望实验新功能的开发者可能会使用 nightly 或 beta 版。
如下是一个开发和发布过程如何运转的例子:假设 Rust 团队正在进行 Rust 1.5 的发布工作。该版本发布于 2015 年 12 月不过这里只是为了提供一个真实的版本。Rust 新增了一项功能:一个 `master` 分支的新提交。每天晚上,会产生一个新的 nightly 版本。每天都是发布版本的日子,而这些发布由发布基础设施自动完成。所以随着时间推移,发布轨迹看起来像这样,版本一天一发:
如下是一个开发和发布过程如何运转的例子:假设 Rust 团队正在进行 Rust 1.5 的发布工作。该版本发布于 2015 年 12 月不过这里只是为了提供一个真实的版本。Rust 新增了一项功能:一个 `master` 分支的新提交。每天晚上,会产生一个新的 nightly 版本。每天都是发布版本的日子,而这些发布由发布基础设施自动完成。所以随着时间推移,发布轨迹看起来像这样,版本每晚一发:
```text
nightly: * - - * - - *
```
6 周时间是准备发布新版本的时候了Rust 仓库的 `beta` 分支会从用于 nightly 的 `master` 分支产生。现在,有了两个发布版本
周时间是准备发布新版本的时候了Rust 仓库的 `beta` 分支会从用于 nightly 的 `master` 分支产生。现在,有了两个发布渠道
```text
nightly: * - - * - - *
@ -36,7 +35,7 @@ nightly: * - - * - - *
beta: *
```
大部分 Rust 用户不会主要使用 beta 版本,不过在 CI 系统中对 beta 版本进行测试能够帮助 Rust 发现可能的回归缺陷regression。同时仍产生 nightly 发布:
大部分 Rust 用户不会主要使用 beta 版本,不过在 CI 系统中对 beta 版本进行测试能够帮助 Rust 发现可能的回归缺陷regression。同时仍产生 nightly 发布:
```text
nightly: * - - * - - * - - * - - *
@ -44,7 +43,7 @@ nightly: * - - * - - * - - * - - *
beta: *
```
比如我们发现了一个回归缺陷。好消息是在这些缺陷流入稳定发布之前还有一些时间来测试 beta 版本fix 被合并到 `master`,为此 nightly 版本得到了修复,接着这些 fix 将 backport 到 `beta` 分支,一个新的 beta 发布就产生了:
比如我们发现了一个回归缺陷。好消息是在这些回归缺陷流入稳定发布之前还有一些时间来测试 beta 版本fix 被合并到 `master`,为此 nightly 版本得到了修复,接着这些 fix 将 backport 到 `beta` 分支,一个新的 beta 发布就产生了:
```text
nightly: * - - * - - * - - * - - * - - *
@ -52,7 +51,7 @@ nightly: * - - * - - * - - * - - * - - *
beta: * - - - - - - - - *
```
第一个 beta 版的 6 周后,是发布稳定版的时候了!`stable` 分支从 `beta` 分支生成:
第一个 beta 版的周后,是发布稳定版的时候了!`stable` 分支从 `beta` 分支生成:
```text
nightly: * - - * - - * - - * - - * - - * - * - *
@ -62,7 +61,7 @@ beta: * - - - - - - - - *
stable: *
```
好的Rust 1.5 发布了!然而,我们忘了些东西:因为又过了 6 周,我们还需发布 **新版** Rust 的 beta 版Rust 1.6。所以从 `beta` 生成 `stable` 分支后,新版的 `beta` 分支也再次从 `nightly` 生成:
好的Rust 1.5 发布了!然而,我们忘了些东西:因为又过了六周,我们还需发布**下一个** Rust 的 beta 版Rust 1.6。所以从 `beta` 生成 `stable` 分支后,新版的 `beta` 分支也再次从 `nightly` 生成:
```text
nightly: * - - * - - * - - * - - * - - * - * - *
@ -72,29 +71,33 @@ beta: * - - - - - - - - * *
stable: *
```
这被称为 “train model”因为每 6 周,一个版本 “离开车站”“leaves the station”不过从 beta 通道到达稳定通道还一段旅程。
这被称为 “train model”因为每周,一个版本 “离开车站”“leaves the station”不过从 beta 通道到达稳定通道还需历经一段旅程。
Rust 每 6 周发布一个版本,如时钟般准确。如果你知道了某个 Rust 版本的发布时间,就可以知道下个版本的时间:6 周后。每 6 周发布版本的一个好的方面是下一班车会来得更快。如果特定版本碰巧缺失某个功能也无需担心:另一个版本很快就会到来!这有助于减少因临近发版时间而偷偷释出未经完善的功能的压力。
Rust 每周发布一个版本,如时钟般准确。如果你知道了某个 Rust 版本的发布时间,就可以知道下个版本的时间:六周后。每六周发布版本的一个好的方面是下一班车会来得更快。如果特定版本碰巧缺失某个功能也无需担心:另一个版本很快就会到来!这有助于减少因临近发版时间而偷偷释出未经完善的功能的压力。
多亏了这个过程,你总是可以切换到下一版本的 Rust 并验证是否可以轻易的升级:如果 beta 版不能如期工作,你可以向 Rust 团队报告并在发布稳定版之前得到修复beta 版造成的破坏是非常少见的,不过 `rustc` 也不过是一个软件,可能会存在 bug。
多亏了这个过程,你总是可以切换到下一版本的 Rust 并验证是否可以轻易的升级:如果 beta 版不能如期工作,你可以向 Rust 团队报告并在发布稳定版之前得到修复beta 版造成的破坏是非常少见的,不过 `rustc` 也不过是一个软件,难免会有 bug。
### 维护时间
Rust 项目仅对最近的稳定版本提供支持。当发布新稳定版本时旧版本即达到生命周期终止EOL, end of life这意味着每个版本的支持期为六周。
### 不稳定功能
这个发布模型中另一个值得注意的地方不稳定功能unstable features。Rust 使用一个被称为 “功能标记”“feature flags”的技术来确定给定版本的某个功能是否启用。如果新功能正在积极地开发中其提交到了 `master`,因此会出现在 nightly 版中,不过会位于一个 **功能标记** 之后。作为用户,如果你希望尝试这个正在开发的功能,则可以在源码中使用合适的标记来开启,不过必须使用 nightly 版。
这个发布模型中另一个值得注意的地方不稳定功能unstable features。Rust 使用一个被称为 “功能标记”“feature flags”的技术来确定给定版本的某个功能是否启用。如果新功能正在积极地开发中其提交到了 `master`,因此会出现在 nightly 版中,不过会位于一个 **功能标记** 之后。作为用户,如果你希望尝试这个正在开发的功能,必须使用 nightly 版并在源码中使用合适的标记来开启
如果使用的是 beta 或稳定版 Rust则不能使用任何功能标记。这是在新功能被宣布为永久稳定之前获得实用价值的关键。这既满足了希望使用最尖端技术的同学,那些坚持稳定版的同学也知道其代码不会被破坏。这就是无停滞稳定。
如果使用的是 beta 或稳定版 Rust则不能使用任何功能标记。这是在新功能被宣布为永久稳定之前让大家提前实际使用它们的关键。这既满足了希望使用最尖端技术的同学,那些坚持稳定版的同学也知道其代码不会被破坏。这就是无停滞稳定。
本书只包含稳定的功能,因为还在开发中的功能仍可能改变,当其进入稳定版时肯定会与编写本书的时候有所不同。你可以在网上获取 nightly 版的文档。
本书只包含稳定的功能,因为还在开发中的功能仍可能改变,当其进入稳定版时肯定会与编写本书的时候有所不同。你可以在网上获取只存在 nightly 版中功能的文档。
### Rustup 和 Rust Nightly 的职责
Rustup 使得改变不同发布通道的 Rust 更为简单,其在全局或分项目的层次工作。其默认会安装稳定版 Rust。例如为了安装 nightly
Rustup 使得改变不同发布通道的 Rust 更为简单,其在全局或分项目的层次工作。其默认会安装稳定版 Rust。例如为了安装 nightly
```console
$ rustup toolchain install nightly
```
你会发现 `rustup` 也安装了所有的 **工具链**_toolchains_Rust 和其相关组件)。如下是一位作者的 Windows 计算机上的例子:
你会发现 `rustup` 也安装了所有的**工具链**_toolchains_Rust 和其相关组件)。如下是一位作者的 Windows 计算机上的例子:
```powershell
> rustup toolchain list
@ -110,14 +113,14 @@ $ cd ~/projects/needs-nightly
$ rustup override set nightly
```
现在,每次在 _~/projects/needs-nightly_ 调用 `rustc``cargo``rustup` 会确保使用 nightly 版 Rust。在你有很多 Rust 项目时大有裨益!
现在,每次在 _~/projects/needs-nightly_ 调用 `rustc``cargo``rustup` 会确保使用 nightly 版 Rust 而非默认的稳定版。在你有很多 Rust 项目时大有裨益!
### RFC 程和团队
### RFC 程和团队
那么你如何了解这些新功能呢Rust 开发模式遵循一个 **Request For Comments (RFC) 过程**。如果你希望改进 Rust可以编写一个提议,也就是 RFC。
那么你如何了解这些新功能呢Rust 开发模式遵循一个 **Request For Comments (RFC) 流程**。如果你希望改进 Rust可以编写一个提案,也就是 RFC。
任何人都可以编写 RFC 来改进 Rust同时这些 RFC 会被 Rust 团队评审和讨论,他们由很多不同分工的子团队组成。这里是 [Rust 官网上](https://www.rust-lang.org/governance) 所有团队的总列表,其包含了项目中每个领域的团队:语言设计、编译器实现、基础设施、文档等。各个团队会阅读相应的提议和评论,编写回复,并最终达成接受或回绝功能的一致。
任何人都可以编写 RFC 来改进 Rust同时这些 RFC 会被 Rust 团队评审和讨论,他们由很多不同分工的子团队组成。这里是 [Rust 官网上](https://www.rust-lang.org/governance) 所有团队的总列表,其包含了项目中每个领域的团队:语言设计、编译器实现、基础设施、文档等。各个团队会阅读相应的提议和评论,发表自己的意见,并最终达成接受或回绝功能的一致。
如果功能被接受了,在 Rust 仓库会打开一个 issue人们就可以实现它。实现功能的人当然可能不是最初提议功能的人当实现完成后其会合并到 `master` 分支并位于一个功能开关feature gate之后正如 [“不稳定功能”](#不稳定功能) 部分所讨论的。
在稍后的某个时间,一旦使用 nightly 版的 Rust 团队能够尝试这个功能了,团队成员会讨论这个功能,它如何在 nightly 中工作,并决定是否应该进入稳定版。如果决定继续推进,功能开关会移除,然后这个功能就被认为是稳定的了!乘着“发布列车”,最终在新的稳定版 Rust 中出现。
在稍后的某个时间,一旦使用 nightly 版的 Rust 团队能够尝试这个功能了,团队成员会讨论这个功能,它如何在 nightly 中工作,并决定是否应该进入稳定版。如果决定继续推进,功能开关会移除,然后这个功能就被认为是稳定的了!乘着“发布列车”,最终在新的稳定版 Rust 中出现。

@ -1,13 +1,12 @@
# 模式与模式匹配
> [ch19-00-patterns.md](https://github.com/rust-lang/book/blob/main/src/ch19-00-patterns.md)
> <br>
> commit 6fce661a0938aa0da06526e7b8f98fd7e67a222f
<!-- https://github.com/rust-lang/book/blob/main/src/ch19-00-patterns.md -->
<!-- commit 3a30e4c1fbe641afc066b3af9eb01dcdf5ed8b24 -->
**模式***Patterns*)是 Rust 中特殊的语法,它用来匹配类型的结构,无论类型是简单还是复杂。结合使用模式和 `match` 表达式以及其他结构可以提供更多对程序控制流的支配权。模式由如下一些内容组合而成:
**模式***Patterns*)是 Rust 中一种特殊的语法,它用来匹配类型的结构,无论类型是简单还是复杂。结合使用模式和 `match` 表达式以及其他结构可以提供更多对程序控制流的支配权。模式由如下一些内容组合而成:
* 字面值
* 解构的数组、枚举、结构体或者元组
* 解构的数组、枚举、结构体或者元组
* 变量
* 通配符
* 占位符
@ -16,4 +15,4 @@
我们通过将一些值与模式相比较来使用它。如果模式匹配这些值,我们对值部分进行相应处理。回忆一下第六章讨论 `match` 表达式时像硬币分类器那样使用模式。如果数据符合这个形状,就可以使用这些命名的片段。如果不符合,与该模式相关的代码则不会运行。
本章是所有模式相关内容的参考。我们将涉及到使用模式的有效位置,*refutable* 与 *irrefutable* 模式的区别,和你可能会见到的不同类型的模式语法。在最后,你将会看到如何使用模式创建强大而简洁的代码
本章是所有模式相关内容的参考。我们将涉及到使用模式的有效位置,*refutable* 与 *irrefutable* 模式的区别,和你可能会见到的不同类型的模式语法。到本章末尾,你就能掌握如何利用模式以清晰的方式表达多种概念

@ -1,14 +1,13 @@
## 所有可能会用到模式的位置
> [ch19-01-all-the-places-for-patterns.md](https://github.com/rust-lang/book/blob/main/src/ch19-01-all-the-places-for-patterns.md)
> <br>
> commit 0c2d41e2afce734825c3a12087d423e8c2f0ae53
<!-- https://github.com/rust-lang/book/blob/main/src/ch19-01-all-the-places-for-patterns.md -->
<!-- commit 56ec353290429e6547109e88afea4de027b0f1a9 -->
模式出现在 Rust 的很多地方。你已经在不经意间使用了很多模式!本部分是一个所有有效模式位置的参考
模式出现在 Rust 的很多地方。你已经在不经意间使用了很多模式!本节将介绍所有模式有效的位置
### `match` 分支
如第六章所讨论的,一个模式常用的位置是 `match` 表达式的分支。在形式上 `match` 表达式由 `match` 关键字、用于匹配的值和一个或多个分支构成,这些分支包含一个模式和在值匹配分支的模式时运行的表达式:
如第六章所讨论的,一个模式常用的位置是 `match` 表达式的分支。在形式上 `match` 表达式由 `match` 关键字、用于匹配的值和一个或多个分支构成,这些分支包含一个模式和在值匹配分支的模式时运行的表达式,如下所示
```text
match VALUE {
@ -29,7 +28,7 @@ match x {
这个 `match` 表达式中的模式为每个箭头左边的 `None``Some(i)`
`match` 表达式必须**穷尽***exhaustive*)的,意为 `match` 表达式所有可能的值都必须被考虑到。一个确保覆盖每个可能值的方法是在最后一个分支使用捕获所有的模式:比如,一个匹配任何值的名称永远也不会失败,因此可以覆盖所有匹配剩下的情况。
`match` 表达式的一个要求是它们必须**穷尽***exhaustive*)的,意为 `match` 表达式所有可能的值都必须被考虑到。一个确保覆盖每个可能值的方法是在最后一个分支使用捕获所有的模式:比如,一个匹配任何值的名称永远也不会失败,因此可以覆盖所有匹配剩下的情况。
有一个特定的模式 `_` 可以匹配所有情况,不过它从不绑定任何变量。这在例如希望忽略任何未指定值的情况很有用。本章之后的 [“忽略模式中的值”][ignoring-values-in-a-pattern] 部分会详细介绍 `_` 模式的更多细节。
@ -39,7 +38,7 @@ match x {
示例 19-1 展示了也可以组合并匹配 `if let`、`else if` 和 `else if let` 表达式。这相比 `match` 表达式一次只能将一个值与模式比较提供了更多灵活性。并且 Rust 并不要求一系列 `if let`、`else if`、`else if let` 分支的条件相互关联。
示例 19-1 中的代码展示了一系列针对不同条件的检查来决定背景颜色应该是什么。为了达到这个例子的目的,我们创建了硬编码值的变量,在真实程序中则可能由询问用户获得
示例 19-1 中的代码展示了一系列针对不同条件的检查来决定背景颜色应该是什么。为了达到这个例子的目的,我们创建了硬编码值的变量,真实程序中这些值可能来源于用户输入
<span class="filename">文件名src/main.rs</span>
@ -53,13 +52,13 @@ match x {
这个条件结构允许我们支持复杂的需求。使用这里硬编码的值,例子会打印出 `Using purple as the background color`
注意 `if let` 也可以像 `match` 分支那样引入覆盖变量:`if let Ok(age) = age` 引入了一个新的覆盖变量 `age`,它包含 `Ok` 成员中的值。这意味着 `if age > 30` 条件需要位于这个代码块内部;不能将两个条件组合为 `if let Ok(age) = age && age > 30`,因为我们希望与 30 进行比较的被覆盖的 `age` 直到大括号开始的新作用域才是有效的
注意 `if let` 也可以像 `match` 分支那样引入并遮蔽现有变量:`if let Ok(age) = age` 引入了一个新的 `age` 变量,包含 `Ok` 变体中的值,从而遮蔽了之前的 `age` 变量。这意味着 `if age > 30` 条件需要位于这个代码块内部:不能将两个条件组合为 `if let Ok(age) = age && age > 30`,因为我们想与 30 比较的新 `age` 只有在大括号开启的新作用域内才有效
`if let` 表达式的缺点在于其穷尽性没有为编译器所检查,而 `match` 表达式则检查了。如果去掉最后的 `else` 块而遗漏处理一些情况,编译器也不会警告这类可能的逻辑错误。
### `while let` 条件循环
一个与 `if let` 结构类似的是 `while let` 条件循环,它允许只要模式匹配就一直进行 `while` 循环。我们在第十七章第一次见到 `while let` 循环,当时我们使用它在流还在产生新值的时候保持循环。同理在示例 19-2 展示了一个 `while let` 循环等待跨线程发送的消息,不过在这个示例中它检查一个 `Result`不是 `Option`
一个与 `if let` 结构类似的是 `while let` 条件循环,它允许只要模式匹配就一直进行 `while` 循环。在示例 19-2 展示了一个 `while let` 循环等待跨线程发送的消息,不过在这个示例中它检查一个 `Result` `Option`
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-02/src/main.rs:here}}
@ -67,11 +66,11 @@ match x {
<span class="caption">示例 19-2: 使用 `while let` 循环只要 `rx.recv()` 返回 `Ok` 就打印出其值</span>
这个例子会打印出 1、2 和 3。当在第十六章遇到 `recv` 时,我们直接 unwrap 了错误,或者使用 `for` 循环将其视为迭代器处理。不过如示例 19-2 所示,我们也可以使用 `while let`,因为 `recv` 方法只要发送端持续产生消息它就一直返回 `Ok`,并在发送端断开连接后产生一个 `Err`
这个例子会打印出 `1``2``3`。`recv` 方法从信道的接收端取出第一条消息并返回一个 `Ok(value)`。当在第十六章遇到 `recv` 时,我们直接 unwrap 了错误,或者使用 `for` 循环将其视为迭代器处理。不过如示例 19-2 所示,我们也可以使用 `while let`,因为 `recv` 方法只要发送端持续产生消息它就一直返回 `Ok`,并在发送端断开连接后产生一个 `Err`
### `for` 循环
`for` 循环中,模式是 `for` 关键字直接跟随的值,正如 `for x in y` 中的 `x`。示例 19-3 中展示了如何使用 `for` 循环来解构,或拆开一个元组作为 `for` 循环的一部分:
`for` 循环中,模式是 `for` 关键字直接跟随的值。例如,在 `for x in y` 中,`x` 就是这个模式。示例 19-3 中展示了如何使用 `for` 循环来解构,或拆开一个元组作为 `for` 循环的一部分:
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-03/src/main.rs:here}}
@ -85,11 +84,11 @@ match x {
{{#include ../listings/ch19-patterns-and-matching/listing-19-03/output.txt}}
```
这里使用 `enumerate` 方法适配一个迭代器来产生一个值和其在迭代器中的索引,它们位于一个元组中。第一个产生的值是元组 `(0, 'a')`。当这个值匹配模式 `(index, value)``index` 将会是 0 而 `value` 将会是 `'a'`,并打印出第一行输出。
这里使用 `enumerate` 方法适配一个迭代器来产生一个值和其在迭代器中的索引,它们位于一个元组中。第一个产生的值是元组 `(0, 'a')`。当这个值匹配模式 `(index, value)``index` 将会是 `0``value` 将会是 `'a'`,并打印出第一行输出。
### `let` 语句
在本章之前,我们只明确讨论过通过 `match``if let` 使用模式,不过事实上也在别的地方使用过模式,包括 `let` 语句。例如,考虑一下这个直白的 `let` 变量赋值:
在本章之前,我们只明确讨论过通过 `match``if let` 使用模式,不过事实上也在别的地方使用过模式,包括 `let` 语句。例如,考虑一下这个直白的 `let` 变量赋值:
```rust
let x = 5;
@ -111,7 +110,7 @@ let PATTERN = EXPRESSION;
<span class="caption">示例 19-4: 使用模式解构元组并一次创建三个变量</span>
这里将一个元组与模式匹配。Rust 会比较值 `(1, 2, 3)` 与模式 `(x, y, z)` 并发现此值匹配这个模式。在这个例子中,将会把 `1` 绑定到 `x``2` 绑定到 `y``3` 绑定到 `z`。你可以将这个元组模式看作是将三个独立的变量模式结合在一起。
这里将一个元组与模式匹配。Rust 会比较值 `(1, 2, 3)` 与模式 `(x, y, z)`,并发现二者具有相同的元素数量,因此匹配成功,于是将 `1` 绑定到 `x`,将 `2` 绑定到 `y``3` 绑定到 `z`。你可以将这个元组模式看作是将三个独立的变量模式结合在一起。
如果模式中元素的数量不匹配元组中元素的数量,则整个类型不匹配,并会得到一个编译时错误。例如,示例 19-5 展示了尝试用两个变量解构三个元素的元组,这是不行的:
@ -139,7 +138,7 @@ let PATTERN = EXPRESSION;
<span class="caption">列表 19-6: 在参数中使用模式的函数签名</span>
`x` 部分就是一个模式!类似于之前对 `let` 所做的,可以在函数参数中匹配元组。列表 19-7 将传递给函数的元组拆分为值:
`x` 部分就是一个模式!类似于之前对 `let` 所做的,可以在函数参数中匹配元组。列表 19-7 将传递给函数的元组拆分为各个值:
<span class="filename">文件名src/main.rs</span>
@ -149,7 +148,7 @@ let PATTERN = EXPRESSION;
<span class="caption">列表 19-7: 一个在参数中解构元组的函数</span>
这会打印出 `Current location: (3, 5)`。值 `&(3, 5)` 会匹配模式 `&(x, y)`,如此 `x` 得到了值 `3`,而 `y`得到了值 `5`
这会打印出 `Current location: (3, 5)`。值 `&(3, 5)` 会匹配模式 `&(x, y)`,如此 `x` 得到了值 `3`,而 `y` 得到了值 `5`
因为如第十三章所讲闭包类似于函数,也可以在闭包参数列表中使用模式。

@ -1,22 +1,21 @@
## Refutability可反驳性: 模式是否会匹配失效
> [ch19-02-refutability.md](https://github.com/rust-lang/book/blob/main/src/ch19-02-refutability.md)
> <br>
> commit 0c2d41e2afce734825c3a12087d423e8c2f0ae53
<!-- https://github.com/rust-lang/book/blob/main/src/ch19-02-refutability.md -->
<!-- commit 56ec353290429e6547109e88afea4de027b0f1a9 -->
模式有两种形式refutable可反驳的和 irrefutable不可反驳的。能匹配任何传递的可能值的模式被称为是 **不可反驳的***irrefutable*)。一个例子就是 `let x = 5;` 语句中的 `x`,因为 `x` 可以匹配任何值所以不可能会失败。对某些可能的值进行匹配会失败的模式被称为是 **可反驳的***refutable*)。一个这样的例子便是 `if let Some(x) = a_value` 表达式中的 `Some(x)`;如果变量 `a_value` 中的值是 `None` 而不是 `Some`,那么 `Some(x)` 模式不能匹配。
模式有两种形式refutable可反驳的和 irrefutable不可反驳的。能匹配任何传递的可能值的模式被称为是**不可反驳的***irrefutable*)。一个例子就是 `let x = 5;` 语句中的 `x`,因为 `x` 可以匹配任何值所以不可能会失败。对某些可能的值进行匹配会失败的模式被称为是**可反驳的***refutable*)。一个这样的例子便是 `if let Some(x) = a_value` 表达式中的 `Some(x)`;如果变量 `a_value` 中的值是 `None` 而不是 `Some`,那么 `Some(x)` 模式不能匹配。
函数参数、`let` 语句和 `for` 循环只能接受不可反驳的模式,因为当值不匹配时,程序无法进行有意义的操作。`if let` 和 `while let` 表达式可以接受可反驳和不可反驳的模式,但编译器会对不可反驳的模式发出警告,因为根据定义它们旨在处理可能的失败:条件表达式的功能在于它能够根据成功或失败来执行不同的操作。
通常我们无需担心可反驳和不可反驳模式的区别,不过确实需要熟悉可反驳性的概念,这样当在错误信息中看到时就知道如何应对。遇到这些情况,根据代码行为的意图,需要修改模式或者使用模式的结构。
让我们看看一个尝试在 Rust 要求不可反驳模式的地方使用可反驳模式以及相反情况的例子。在示例 18-8 中,有一个 `let` 语句,不过模式被指定为可反驳模式 `Some(x)`。如你所见,这不能编译:
让我们看看一个尝试在 Rust 要求不可反驳模式的地方使用可反驳模式以及相反情况的例子。在示例 19-8 中,有一个 `let` 语句,不过模式被指定为可反驳模式 `Some(x)`。如你所见,这不能编译:
```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-08/src/main.rs:here}}
```
<span class="caption">示例 18-8: 尝试在 `let` 中使用可反驳模式</span>
<span class="caption">示例 19-8: 尝试在 `let` 中使用可反驳模式</span>
如果 `some_option_value` 的值是 `None`,其不会成功匹配模式 `Some(x)`,表明这个模式是可反驳的。然而,因为 `let` 对于 `None` 匹配不能产生任何合法的代码,所以 `let` 语句只能接受不可反驳模式。Rust 会在编译时抱怨我们尝试在要求不可反驳模式的地方使用可反驳模式:
@ -26,21 +25,22 @@
因为我们没有覆盖(也不可能覆盖!)到模式 `Some(x)` 的每一个可能的值,所以 Rust 会合理地抗议。
为了修复在需要不可反驳模式的地方使用可反驳模式的情况,可以修改使用模式的代码:不同于使用 `let`,可以使用 `if let`。如此,如果模式不匹配,大括号中的代码将被忽略,其余代码保持有效。示例 18-9 展示了如何修复示例 18-8 中的代码。
为了修复在需要不可反驳模式的地方使用可反驳模式的情况,可以修改使用模式的代码:不同于使用 `let`,可以使用 `if let`。如此,如果模式不匹配,大括号中的代码将被忽略,其余代码保持有效。示例 19-9 展示了如何修复示例 19-8 中的代码。
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-09/src/main.rs:here}}
```
<span class="caption">示例 18-9: 使用 `if let` 和一个带有可反驳模式的代码块来代替 `let`</span>
<span class="caption">示例 19-9: 使用 `if let` 和一个带有可反驳模式的代码块来代替 `let`</span>
我们给了代码一个得以继续的出路!虽然我们没办法在避免产生错误的情况下使用不可反驳模式,但这段使用可反驳模式的代码是完全有效的。如果为 `if let` 提供了一个总是会匹配的模式,比如示例 18-10 中的 `x`,编译器会给出一个警告:
我们给代码留了一条后路!现在这段代码已经完全有效了。然而,如果我们给 `if let` 提供一个不可反驳模式(即总会匹配的模式),例如示例 19-10 中的 `x`,编译器就会给出警告:
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-10/src/main.rs:here}}
```
<span class="caption">示例 18-10: 尝试把不可反驳模式用到 `if let`</span>
<span class="caption">示例 19-10: 尝试把不可反驳模式用到 `if let`</span>
Rust 会抱怨将不可反驳模式用于 `if let` 是没有意义的:
@ -48,6 +48,6 @@ Rust 会抱怨将不可反驳模式用于 `if let` 是没有意义的:
{{#include ../listings/ch19-patterns-and-matching/listing-19-10/output.txt}}
```
基于此,`match`匹配分支必须使用可反驳模式除了最后一个分支需要使用能匹配任何剩余值的不可反驳模式。Rust 允许我们在只有一个匹配分支的`match`中使用不可反驳模式,不过这么做不是特别有用,并可以被更简单的 `let` 语句替代。
基于此,`match` 匹配分支必须使用可反驳模式除了最后一个分支需要使用能匹配任何剩余值的不可反驳模式。Rust 允许我们在只有一个匹配分支的`match` 中使用不可反驳模式,不过这么做不是特别有用,并可以被更简单的 `let` 语句替代。
目前我们已经讨论了所有可以使用模式的地方,以及可反驳模式与不可反驳模式的区别,下面让我们一起去把可以用来创建模式的语法过目一遍吧。

@ -1,8 +1,7 @@
## 所有的模式语法
## 模式语法
> [ch19-03-pattern-syntax.md](https://github.com/rust-lang/book/blob/main/src/ch19-03-pattern-syntax.md)
> <br>
> commit 6fce661a0938aa0da06526e7b8f98fd7e67a222f
<!-- https://github.com/rust-lang/book/blob/main/src/ch19-03-pattern-syntax.md -->
<!-- commit 56ec353290429e6547109e88afea4de027b0f1a9 -->
在本节中,我们收集了模式中所有有效的语法,并讨论为什么以及何时你可能要使用这些语法。
@ -18,7 +17,7 @@
### 匹配命名变量
命名变量是匹配任何值的不可反驳模式,这在之前已经使用过数次。然而当其用于 `match` 表达式时情况会有些复杂。因为 `match` 会开始一个新作用域,`match` 表达式中作为模式的一部分声明的变量会覆盖 `match` 结构之外的同名变量,与所有变量一样。在示例 18-11 中,声明了一个值为 `Some(5)` 的变量 `x` 和一个值为 `10` 的变量 `y`。接着在值 `x` 上创建了一个 `match` 表达式。观察匹配分支中的模式和结尾的 `println!`,并在运行此代码或进一步阅读之前推断这段代码会打印什么。
命名变量Named variables是匹配任何值的不可反驳模式,这在之前已经使用过数次。然而,当在 `match`、`if let` 或 `while let` 表达式中使用命名变量时,会出现一些复杂情况。由于这些表达式会开始一个新作用域,作为模式一部分在表达式内部声明的变量会遮蔽外部同名变量,这与所有变量的遮蔽规则一致。在示例 19-11 中,声明了一个值为 `Some(5)` 的变量 `x` 和一个值为 `10` 的变量 `y`。接着在值 `x` 上创建了一个 `match` 表达式。观察匹配分支中的模式和结尾的 `println!`,并在运行此代码或进一步阅读之前推断这段代码会打印什么。
<span class="filename">文件名src/main.rs</span>
@ -26,21 +25,21 @@
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-11/src/main.rs:here}}
```
<span class="caption">示例 18-11: 一个 `match` 语句其中一个分支引入了覆盖变量 `y`</span>
<span class="caption">示例 19-11: 一个 `match` 语句其中一个分支引入了遮蔽变量 `y`</span>
让我们看看当 `match` 语句运行的时候发生了什么。第一个匹配分支的模式并不匹配 `x` 中定义的值,所以代码继续执行。
第二个匹配分支中的模式引入了一个新变量 `y`,它会匹配任何 `Some` 中的值。因为我们在 `match` 表达式的新作用域中,这是一个新变量,而不是开头声明为值 10 的那个 `y`。这个新的 `y` 绑定会匹配任何 `Some` 中的值,在这里是 `x` 中的值。因此这个 `y` 绑定了 `x``Some` 内部的值。这个值是 5所以这个分支的表达式将会执行并打印出 `Matched, y = 5`
如果 `x` 的值是 `None` 而不是 `Some(5)`,头两个分支的模式不会匹配,所以会匹配下划线。这个分支的模式中没有引入变量 `x`,所以此时表达式中的 `x` 会是外部没有被覆盖`x`。在这个假想的例子中,`match` 将会打印 `Default case, x = None`
如果 `x` 的值是 `None` 而不是 `Some(5)`,头两个分支的模式不会匹配,所以会匹配下划线。这个分支的模式中没有引入变量 `x`,所以此时表达式中的 `x` 会是外部没有被遮蔽`x`。在这个假想的例子中,`match` 将会打印 `Default case, x = None`
一旦 `match` 表达式执行完毕,其作用域也就结束了,同理内部 `y` 的作用域也结束了。最后的 `println!` 会打印 `at the end: x = Some(5), y = 10`
为了创建能够比较外部 `x``y` 的值,而不引入覆盖变量`match` 表达式我们需要相应地使用带有条件的匹配守卫match guard。我们稍后将在 [“匹配守卫提供的额外条件”](#匹配守卫提供的额外条件) 这一小节讨论匹配守卫。
为了创建能够比较外部 `x``y` 的值,又不引入新的变量去遮蔽已有 `y` `match` 表达式我们需要相应地使用带有条件的匹配守卫match guard。我们稍后将在 [“匹配守卫提供的额外条件”](#匹配守卫提供的额外条件) 这一小节讨论匹配守卫。
### 多个模式
`match` 表达式中,可以使用 `|` 语法匹配多个模式,它代表 **或**_or_运算符模式。例如如下代码将 `x` 的值与匹配分支相比较,第一个分支有 **或** 选项,意味着如果 `x` 的值匹配此分支的任一个值,它就会运行:
`match` 表达式中,可以使用 `|` 语法匹配多个模式,它代表 **或**_or_运算符模式。例如如下代码将 `x` 的值与匹配分支相比较,第一个分支有**或**选项,意味着如果 `x` 的值匹配此分支的任一个值,它就会运行:
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/no-listing-02-multiple-patterns/src/main.rs:here}}
@ -48,9 +47,9 @@
上面的代码会打印 `one or two`
### 通过 `..=` 匹配值范围
### 通过 `..=` 匹配值范围
`..=` 语法允许你匹配一个闭区间范围内的值。在如下代码中,当模式匹配任何在给定范围内的值时,该分支会执行:
`..=` 语法允许你匹配一个闭区间范围range内的值。在如下代码中,当模式匹配任何在给定范围内的值时,该分支会执行:
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/no-listing-03-ranges/src/main.rs:here}}
@ -74,7 +73,7 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
#### 解构结构体
示例 18-12 展示带有两个字段 `x``y` 的结构体 `Point`,可以通过带有模式的 `let` 语句将其分解:
示例 19-12 展示带有两个字段 `x``y` 的结构体 `Point`,可以通过带有模式的 `let` 语句将其分解:
<span class="filename">文件名src/main.rs</span>
@ -82,9 +81,9 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-12/src/main.rs}}
```
<span class="caption">示例 18-12: 解构一个结构体的字段为单独的变量</span>
<span class="caption">示例 19-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;` 包含了很多重复,所以对于匹配结构体字段的模式存在简写:只需列出结构体字段的名称,则模式创建的变量会有相同的名称。示例 19-13 展示了与示例 19-12 有着相同行为的代码,不过 `let` 模式创建的变量为 `x``y` 而不是 `a``b`
<span class="filename">文件名src/main.rs</span>
@ -92,13 +91,13 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-13/src/main.rs}}
```
<span class="caption">示例 18-13: 使用结构体字段简写来解构结构体字段</span>
<span class="caption">示例 19-13: 使用结构体字段简写来解构结构体字段</span>
这段代码创建了变量 `x``y`,与变量 `p` 中的 `x``y` 相匹配。其结果是变量 `x``y` 包含结构体 `p` 中的值。
也可以使用字面值作为结构体模式的一部分进行解构,而不是为所有的字段创建变量。这允许我们测试一些字段为特定值的同时创建其他字段的变量。
示例 18-14 展示了一个 `match` 语句将 `Point` 值分成了三种情况:直接位于 `x` 轴上(此时 `y = 0` 为真)、位于 `y` 轴上(`x = 0`)或不在任何轴上的点。
示例 19-14 展示了一个 `match` 语句将 `Point` 值分成了三种情况:直接位于 `x` 轴上(此时 `y = 0` 为真)、位于 `y` 轴上(`x = 0`)或不在任何轴上的点。
<span class="filename">文件名src/main.rs</span>
@ -106,19 +105,19 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-14/src/main.rs:here}}
```
<span class="caption">示例 18-14: 解构和匹配模式中的字面值</span>
<span class="caption">示例 19-14: 解构和匹配模式中的字面值</span>
第一个分支通过指定字段 `y` 匹配字面值 `0` 来匹配任何位于 `x` 轴上的点。此模式仍然创建了变量 `x` 以便在分支的代码中使用。
类似的,第二个分支通过指定字段 `x` 匹配字面值 `0` 来匹配任何位于 `y` 轴上的点,并为字段 `y` 创建了变量 `y`。第三个分支没有指定任何字面值,所以其会匹配任何其他的 `Point` 并为 `x``y` 两个字段创建变量。
在这个例子中,值 `p` 因为其 `x` 包含 0 而匹配第二个分支,因此会打印出 `On the y axis at 7`
在这个例子中,值 `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不过当时没有明确提到解构枚举的模式需要对应枚举所定义的储存数据的方式。让我们以示例 6-2 中的 `Message` 枚举为例,编写一个 `match` 使用模式解构每一个内部值,如示例 18-15 所示:
本书之前曾经解构过枚举(例如第六章示例 6-5不过当时没有明确提到解构枚举的模式需要对应枚举所定义的储存数据的方式。让我们以示例 6-2 中的 `Message` 枚举为例,编写一个 `match` 使用模式解构每一个内部值,如示例 19-15 所示:
<span class="filename">文件名src/main.rs</span>
@ -126,27 +125,27 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-15/src/main.rs}}
```
<span class="caption">示例 18-15: 解构包含不同类型值成员的枚举</span>
<span class="caption">示例 19-15: 解构包含不同类型值变体的枚举</span>
这段代码会打印出 `Change the color to red 0, green 160, and blue 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` 这样包含三个元素的类元组枚举变体,其模式则类似于用于解构元组的模式。模式中变量的数量必须与变体中元素的数量完全一致。
#### 解构嵌套的结构体和枚举
目前为止,所有的例子都只匹配了深度为一级的结构体或枚举,不过当然也可以匹配嵌套的项!例如,我们可以重构列表 18-15 的代码在 `ChangeColor` 消息中同时支持 RGB 和 HSV 色彩模式,如示例 18-16 所示:
目前为止,所有的例子都只匹配了深度为一级的结构体或枚举,不过当然也可以匹配嵌套的项!例如,我们可以重构列表 19-15 的代码在 `ChangeColor` 消息中同时支持 RGB 和 HSV 色彩模式,如示例 19-16 所示:
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-16/src/main.rs}}
```
<span class="caption">示例 18-16: 匹配嵌套的枚举</span>
<span class="caption">示例 19-16: 匹配嵌套的枚举</span>
`match` 表达式第一个分支的模式匹配一个包含 `Color::Rgb` 枚举成员的 `Message::ChangeColor` 枚举成员,然后模式绑定了 3 个内部的 `i32` 值。第二个分支的模式也匹配一个 `Message::ChangeColor` 枚举成员,但是其内部的枚举会匹配 `Color::Hsv` 枚举成员。我们可以在一个 `match` 表达式中指定这些复杂条件,即使会涉及到两个枚举。
`match` 表达式第一个分支的模式匹配一个包含 `Color::Rgb` 枚举变体的 `Message::ChangeColor` 枚举变体,然后模式绑定了三个内部的 `i32` 值。第二个分支的模式也匹配一个 `Message::ChangeColor` 枚举变体,但是其内部的枚举会匹配 `Color::Hsv` 枚举变体。我们可以在一个 `match` 表达式中指定这些复杂条件,即使会涉及到两个枚举。
#### 解构结构体和元组
@ -158,15 +157,15 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
这将复杂的类型分解成部分组件以便可以单独使用我们感兴趣的值。
通过模式解构是一个方便利用部分值片段的手段,比如结构体中每个单独字段的值。
通过模式解构是一个方便将值的各个片段分离开来单独使用的方式,比如结构体中每个单独字段的值。
### 忽略模式中的值
有时忽略模式中的一些值是有用的,比如 `match` 中最后捕获全部情况的分支实际上没有做任何事,但是它确实对所有剩余情况负责。有一些简单的方法可以忽略模式中全部或部分值:使用 `_` 模式(我们已经见过了),在另一个模式中使用 `_` 模式,使用一个以下划线开始的名称,或者使用 `..` 忽略所剩部分的值。让我们来分别探索如何以及为什么要这么做。
有时忽略模式中的一些值是有用的,比如 `match` 中最后捕获全部情况的分支实际上没有做任何事,但是它确实负责匹配了所有剩余的可能值。有一些方法可以忽略模式中全部或部分值:使用 `_` 模式(我们已经见过了),在另一个模式中使用 `_` 模式,使用一个以下划线开始的名称,或者使用 `..` 忽略所剩部分的值。让我们来分别探索如何以及为什么要这么做。
#### 使用 `_` 忽略整个值
我们已经使用过下划线作为匹配但不绑定任何值的通配符模式了。虽然这作为 `match` 表达式最后的分支特别有用,也可以将其用于任意模式,包括函数参数中,如示例 18-17 所示:
我们已经使用过下划线作为匹配但不绑定任何值的通配符模式了。虽然这作为 `match` 表达式最后的分支特别有用,也可以将其用于任意模式,包括函数参数中,如示例 19-17 所示:
<span class="filename">文件名src/main.rs</span>
@ -174,7 +173,7 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-17/src/main.rs}}
```
<span class="caption">示例 18-17: 在函数签名中使用 `_`</span>
<span class="caption">示例 19-17: 在函数签名中使用 `_`</span>
这段代码会完全忽略作为第一个参数传递的值 `3`,并会打印出 `This code only uses the y parameter: 4`
@ -182,31 +181,31 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
#### 使用嵌套的 `_` 忽略部分值
也可以在一个模式内部使用`_` 忽略部分值,例如,当只需要测试部分值但在期望运行的代码中没有用到其他部分时。示例 18-18 展示了负责管理设置值的代码。业务需求是用户不允许覆盖现有的自定义设置,但是可以取消设置,也可以在当前未设置时为其提供设置
也可以在一个模式内部使用`_` 忽略部分值,例如,当只需要测试部分值但在期望运行的代码中没有用到其他部分时。示例 19-18 展示了负责管理设置值的代码。业务需求是用户不允许覆盖现有的自定义设置,但是可以取消设置,也可以在当前未设置时为其提供一个值
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-18/src/main.rs:here}}
```
<span class="caption">示例 18-18: 当不需要 `Some` 中的值时在模式内使用下划线来匹配 `Some` 成员</span>
<span class="caption">示例 19-18: 当不需要 `Some` 中的值时在模式内使用下划线来匹配 `Some` 变体</span>
这段代码会打印出 `Can't overwrite an existing customized value` 接着是 `setting is Some(5)`。在第一个匹配分支,我们不需要匹配或使用任一个 `Some` 成员中的值;重要的部分是需要测试 `setting_value``new_setting_value` 都为 `Some` 成员的情况。在这种情况,我们打印出为何不改变 `setting_value`,并且不会改变它。
这段代码会打印出 `Can't overwrite an existing customized value` 接着是 `setting is Some(5)`。在第一个匹配分支,我们不需要匹配或使用任一个 `Some` 变体中的值,但需要检测 `setting_value``new_setting_value` 是否均为 `Some` 变体。在这种情况下,我们打印出为何不改变 `setting_value`,并且不会改变它。
对于所有其他情况(`setting_value` 或 `new_setting_value` 任一为 `None`),这由第二个分支的 `_` 模式体现,这时确实希望允许 `new_setting_value` 变为 `setting_value`
也可以在一个模式中的多处使用下划线来忽略特定值,如示例 18-19 所示,这里忽略了一个五元元组中的第二和第四个值:
也可以在一个模式中的多处使用下划线来忽略特定值,如示例 19-19 所示,这里忽略了一个五元元组中的第二和第四个值:
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-19/src/main.rs:here}}
```
<span class="caption">示例 18-19: 忽略元组的多个部分</span>
<span class="caption">示例 19-19: 忽略元组的多个部分</span>
这会打印出 `Some numbers: 2, 8, 32`,值 4 和 16 会被忽略。
这会打印出 `Some numbers: 2, 8, 32`,值 `4``16` 会被忽略。
#### 通过在名字前以一个 `_` 开头来忽略未使用的变量
#### 通过在变量名开头加 `_` 来忽略未使用的变量
如果你创建了一个变量却不在任何地方使用它Rust 通常会给你一个警告,因为未使用的变量可能会是个 bug。但是有时创建一个还未使用的变量是有用的比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头。示例 18-20 中创建了两个未使用变量,不过当编译代码时只会得到其中一个的警告:
如果你创建了一个变量却不在任何地方使用它Rust 通常会给你一个警告,因为未使用的变量可能会是个 bug。但是有时创建一个还未使用的变量是有用的比如你正在设计原型或刚刚开始一个项目。这时你希望告诉 Rust 不要警告未使用的变量,为此可以用下划线作为变量名的开头。示例 19-20 中创建了两个未使用变量,不过当编译代码时只会得到其中一个的警告:
<span class="filename">文件名src/main.rs</span>
@ -214,41 +213,41 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-20/src/main.rs}}
```
<span class="caption">示例 18-20: 以下划线开始变量名以便去掉未使用变量警告</span>
<span class="caption">示例 19-20: 以下划线开始变量名以便去掉未使用变量警告</span>
这里得到了警告说未使用变量 `y`,不过没有警告说使用 `_x`
这里得到了警告说未使用变量 `y`,不过没有警告说使用 `_x`
注意,只使用 `_` 和使用以下划线开头的名称有些微妙的不同:比如 `_x` 仍会将值绑定到变量,而 `_` 则完全不会绑定。为了展示这个区别的意义,示例 18-21 会产生一个错误。
注意,只使用 `_` 和使用以下划线开头的名称有些微妙的不同:比如 `_x` 仍会将值绑定到变量,而 `_` 则完全不会绑定。为了展示这个区别的意义,示例 19-21 会产生一个错误。
```rust,ignore,does_not_compile
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-21/src/main.rs:here}}
```
<span class="caption">示例 18-21: 以下划线开头的未使用变量仍然会绑定值,它可能会获取值的所有权</span>
<span class="caption">示例 19-21: 以下划线开头的未使用变量仍然会绑定值,它可能会获取值的所有权</span>
我们会得到一个错误,因为 `s` 的值仍然会移动进 `_s`,并阻止我们再次使用 `s`。然而只使用下划线本身,并不会绑定值。示例 18-22 能够无错编译,因为 `s` 没有被移动进 `_`
我们会得到一个错误,因为 `s` 的值仍然会移动进 `_s`,并阻止我们再次使用 `s`。然而只使用下划线本身,并不会绑定值。示例 19-22 能够无错编译,因为 `s` 没有被移动进 `_`
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-22/src/main.rs:here}}
```
<span class="caption">示例 18-22: 单独使用下划线不会绑定值</span>
<span class="caption">示例 19-22: 单独使用下划线不会绑定值</span>
上面的代码能很好的运行;因为没有把 `s` 绑定到任何变量;它没有被移动。
#### 用 `..` 忽略剩余值
对于有多个部分的值,可以使用 `..` 语法来只使用特定部分并忽略其它值,同时避免不得不每一个忽略值列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分。在示例 18-23 中,有一个 `Point` 结构体存放了三维空间中的坐标。在 `match` 表达式中,我们希望只操作 `x` 坐标并忽略 `y``z` 字段的值:
对于有多个部分的值,可以使用 `..` 语法来只使用特定部分并忽略其它值,从而避免不得不每一个忽略值列出下划线。`..` 模式会忽略模式中剩余的任何没有显式匹配的值部分。在示例 19-23 中,有一个 `Point` 结构体存放了三维空间中的坐标。在 `match` 表达式中,我们希望只操作 `x` 坐标并忽略 `y``z` 字段的值:
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-23/src/main.rs:here}}
```
<span class="caption">示例 18-23: 通过使用 `..` 来忽略 `Point` 中除 `x` 以外的字段</span>
<span class="caption">示例 19-23: 通过使用 `..` 来忽略 `Point` 中除 `x` 以外的字段</span>
这里列出了 `x` 值,接着仅仅包含了 `..` 模式。这比不得不列出 `y: _``z: _` 要来得简单,特别是在处理有很多字段的结构体,但只涉及一到两个字段时的情形。
`..` 会扩展为所需要的值的数量。示例 18-24 展示了元组中 `..` 的应用
`..` 会扩展为所需要的值的数量。示例 19-24 展示了如何在元组中使用 `..`
<span class="filename">文件名src/main.rs</span>
@ -256,11 +255,11 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-24/src/main.rs}}
```
<span class="caption">示例 18-24: 只匹配元组中的第一个和最后一个值并忽略掉所有其它值</span>
<span class="caption">示例 19-24: 只匹配元组中的第一个和最后一个值并忽略掉所有其它值</span>
这里用 `first``last` 来匹配第一个和最后一个值。`..` 将匹配并忽略中间的所有值。
然而使用 `..` 必须是无歧义的。如果期望匹配和忽略的值是不明确的Rust 会报错。示例 18-25 展示了一个带有歧义的 `..` 例子,因此其不能编译:
然而使用 `..` 必须是无歧义的。如果期望匹配和忽略的值是不明确的Rust 会报错。示例 19-25 展示了一个带有歧义的 `..` 例子,因此其不能编译:
<span class="filename">文件名src/main.rs</span>
@ -268,9 +267,9 @@ Rust 知道 `'c'` 位于第一个模式的范围内,并会打印出 `early ASC
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-25/src/main.rs}}
```
<span class="caption">示例 18-25: 尝试以有歧义的方式运用 `..`</span>
<span class="caption">示例 19-25: 尝试以有歧义的方式运用 `..`</span>
如果编译上面的例子,会得到下面的错误:
当编译这个示例时,会得到如下错误:
```console
{{#include ../listings/ch19-patterns-and-matching/listing-19-25/output.txt}}
@ -280,23 +279,23 @@ Rust 不可能决定在元组中匹配 `second` 值之前应该忽略多少个
### 匹配守卫提供的额外条件
**匹配守卫**_match guard_是一个指定于 `match` 分支模式之后的额外 `if` 条件,它也必须被满足才能选择此分支。匹配守卫用于表达比单独的模式所能允许的更为复杂的情况。
**匹配守卫**_match guard_是一个指定于 `match` 分支模式之后的额外 `if` 条件,它也必须被满足才能选择此分支。匹配守卫用于表达比单独的模式所能允许的更为复杂的情况。但是注意,它们仅在 `match` 表达式中可用,不能用于 `if let``while let` 表达式。
这个条件可以使用模式中创建的变量。示例 18-26 展示了一个 `match`,其中第一个分支有模式 `Some(x)` 还有匹配守卫 `if x % 2 == 0` (当 `x` 是偶数为真)
这个条件可以使用模式中创建的变量。示例 19-26 展示了一个 `match`,其中第一个分支有模式 `Some(x)` 还有匹配守卫 `if x % 2 == 0` (当 `x` 是偶数时为真)
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-26/src/main.rs:here}}
```
<span class="caption">示例 18-26: 在模式中加入匹配守卫</span>
<span class="caption">示例 19-26: 在模式中加入匹配守卫</span>
上例会打印出 `The number 4 is even`。当 `num` 与模式中第一个分支比较时,因为 `Some(4)` 匹配 `Some(x)` 所以可以匹配。接着匹配守卫检查 `x` 除以 `2` 的余数是否等于 `0`,因为它等于 `0`,所以第一个分支被选择。
相反如果 `num``Some(5)`,因为 `5` 除以 `2` 的余数是 `1` 不等于 `0` 所以第一个分支的匹配守卫为。接着 Rust 会前往第二个分支,这次匹配因为它没有匹配守卫所以会匹配任何 `Some` 成员
相反如果 `num``Some(5)`,因为 `5` 除以 `2` 的余数是 `1` 不等于 `0` 所以第一个分支的匹配守卫为 `false`。接着 Rust 会前往第二个分支,这次匹配因为它没有匹配守卫所以会匹配任何 `Some` 变体
无法在模式中表达类似 `if x % 2 == 0` 的条件,所以通过匹配守卫提供了表达类似逻辑的能力。这种替代表达方式的缺点是,编译器不会尝试为包含匹配守卫的模式检查穷尽性。
在示例 18-11 中,我们提到可以使用匹配守卫来解决模式中变量覆盖的问题,那里 `match` 表达式的模式中新建了一个变量而不是使用 `match` 之外的同名变量。新变量意味着不能够测试外部变量的值。示例 18-27 展示了如何使用匹配守卫修复这个问题。
在示例 19-11 中,我们提到可以使用匹配守卫来解决模式中变量遮蔽的问题,那里 `match` 表达式的模式中新建了一个变量而不是使用 `match` 之外的同名变量。新变量意味着不能够测试外部变量的值。示例 19-27 展示了如何使用匹配守卫修复这个问题。
<span class="filename">文件名src/main.rs</span>
@ -304,21 +303,21 @@ Rust 不可能决定在元组中匹配 `second` 值之前应该忽略多少个
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-27/src/main.rs}}
```
<span class="caption">示例 18-27: 使用匹配守卫来测试与外部变量的相等性</span>
<span class="caption">示例 19-27: 使用匹配守卫来测试与外部变量的相等性</span>
现在这会打印出 `Default case, x = Some(5)`。现在第二个匹配分支中的模式不会引入一个覆盖外部 `y` 的新变量 `y`,这意味着可以在匹配守卫中使用外部的 `y`。相比指定会覆盖外部 `y` 的模式 `Some(y)`,这里指定为 `Some(n)`。此新建的变量 `n` 并没有覆盖任何值,因为 `match` 外部没有变量 `n`
现在这会打印出 `Default case, x = Some(5)`。现在第二个匹配分支中的模式不会引入一个遮蔽外部 `y` 的新变量 `y`,这意味着可以在匹配守卫中使用外部的 `y`。相比指定会遮蔽外部 `y` 的模式 `Some(y)`,这里指定为 `Some(n)`。此新建的变量 `n` 并没有覆盖任何值,因为 `match` 外部没有变量 `n`
匹配守卫 `if n == y` 并不是一个模式所以没有引入新变量。这个 `y` **正是** 外部的 `y` 而不是新的覆盖变量 `y`,这样就可以通过比较 `n``y` 来表达寻找一个与外部 `y` 相同的值的概念了。
匹配守卫 `if n == y` 并不是一个模式所以没有引入新变量。这个 `y` **正是**外部的 `y` 而不是新的遮蔽变量 `y`,这样就可以通过比较 `n``y` 来表达寻找一个与外部 `y` 相同的值了。
也可以在匹配守卫中使用 **或** 运算符 `|` 来指定多个模式,同时匹配守卫的条件会作用于所有的模式。示例 18-28 展示了结合匹配守卫与使用了 `|` 的模式的优先级。这个例子中重要的部分是匹配守卫 `if y` 作用于 `4`、`5` **和** `6`,即使这看起来好像 `if y` 只作用于 `6`
也可以在匹配守卫中使用**或**运算符 `|` 来指定多个模式,同时匹配守卫的条件会作用于所有的模式。示例 19-28 展示了结合匹配守卫与使用了 `|` 的模式的优先级。这个例子中重要的部分是匹配守卫 `if y` 作用于 `4`、`5` **和** `6`,即使这看起来好像 `if y` 只作用于 `6`
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-28/src/main.rs:here}}
```
<span class="caption">示例 18-28: 结合多个模式与匹配守卫</span>
<span class="caption">示例 19-28: 结合多个模式与匹配守卫</span>
这个匹配条件表明此分支值匹配 `x` 值为 `4`、`5` 或 `6` **同时** `y``true` 的情况。运行这段代码时会发生的是第一个分支的模式因 `x``4` 而匹配,不过匹配守卫 `if y`,所以第一个分支不会被选择。代码移动到第二个分支,这会匹配,此程序会打印出 `no`。这是因为 `if` 条件作用于整个 `4 | 5 | 6` 模式,而不仅是最后的值 `6`。换句话说,匹配守卫与模式的优先级关系看起来像这样:
这个匹配条件表明此分支值匹配 `x` 值为 `4`、`5` 或 `6` **同时** `y``true` 的情况。运行这段代码时会发生的是第一个分支的模式因 `x``4` 而匹配,不过匹配守卫 `if y` `false`,所以第一个分支不会被选择。代码移动到第二个分支,这会匹配,此程序会打印出 `no`。这是因为 `if` 条件作用于整个 `4 | 5 | 6` 模式,而不仅是最后的值 `6`。换句话说,匹配守卫与模式的优先级关系看起来像这样:
```text
(4 | 5 | 6) if y => ...
@ -330,23 +329,23 @@ Rust 不可能决定在元组中匹配 `second` 值之前应该忽略多少个
4 | 5 | (6 if y) => ...
```
可以通过运行代码时的情况看出这一点:如果匹配守卫只作用于由 `|` 运算符指定的值列表的最后一个值,这个分支就会匹配且程序会打印出 `yes`
运行代码后,优先级行为就很明显了:如果匹配守卫只作用于由 `|` 运算符指定的值列表的最后一个值,这个分支就会匹配且程序会打印出 `yes`
### `@` 绑定
_at_ 运算符(`@`)允许我们在创建一个存放值的变量的同时测试其值是否匹配模式。示例 18-29 展示了一个例子,这里我们希望测试 `Message::Hello``id` 字段是否位于 `3..=7` 范围内,同时也希望能将其值绑定到 `id_variable` 变量中以便此分支相关联的代码可以使用它。可以将 `id_variable` 命名为 `id`,与字段同名,不过出于示例的目的这里选择了不同的名称。
_at_ 运算符(`@`)允许我们在创建一个存放值的变量的同时测试其值是否匹配模式。示例 19-29 展示了一个例子,这里我们希望测试 `Message::Hello``id` 字段是否位于 `3..=7` 范围内,同时也希望能将其值绑定到 `id_variable` 变量中以便此分支相关联的代码可以使用它。可以将 `id_variable` 命名为 `id`,与字段同名,不过出于示例的目的这里选择了不同的名称。
```rust
{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-29/src/main.rs:here}}
```
<span class="caption">示例 18-29: 使用 `@` 在模式中绑定值的同时测试它</span>
<span class="caption">示例 19-29: 使用 `@` 在模式中绑定值的同时测试它</span>
上例会打印出 `Found an id in range: 5`。通过在 `3..=7` 之前指定 `id_variable @`,我们捕获了任何匹配此范围的值并同时测试其值匹配这个范围模式。
第二个分支只在模式中指定了一个范围,分支相关代码没有一个包含 `id` 字段实际值的变量。`id` 字段的值可以是 10、11 或 12不过这个模式的代码并不知情也不能使用 `id` 字段中的值,因为没有将 `id` 值保存进一个变量。
最后一个分支指定了一个没有范围的变量,此时确实拥有可以用于分支代码的变量 `id`因为这里使用了结构体字段简写语法。不过此分支中没有像头两个分支那样对 `id` 字段的值进行测试:任何值都会匹配此分支
最后一个分支指定了一个没有范围的变量,此时确实拥有可以用于分支代码的变量 `id`因为这里使用了结构体字段简写语法。不过此分支中没有像头两个分支那样对 `id` 字段的值进行测试:任何值都会匹配该模式
使用 `@` 可以在一个模式中同时测试和保存变量值。

@ -1,8 +1,7 @@
# 高级特性
> [ch20-00-advanced-features.md](https://github.com/rust-lang/book/blob/main/src/ch20-00-advanced-features.md)
> <br>
> commit 3a30e4c1fbe641afc066b3af9eb01dcdf5ed8b24
<!-- https://github.com/rust-lang/book/blob/main/src/ch20-00-advanced-features.md -->
<!-- commit 3a30e4c1fbe641afc066b3af9eb01dcdf5ed8b24 -->
现在我们已经学习了 Rust 编程语言中最常用的部分。在第二十一章开始另一个新项目之前,我们先来了解一些你偶尔可能会遇到,但并不会每天都用的语言特性。你可以将本章作为不经意间遇到未知的内容时的参考。本章将要学习的功能在一些非常特定的场景下非常有用。虽然很少会碰到它们,我们希望确保你了解 Rust 提供的所有功能。

Loading…
Cancel
Save