update to ch07-05

main
KaiserY 1 week ago
parent eae61e2b3a
commit 5b2fe9a11c

@ -85,11 +85,11 @@
`Default` trait 使你创建一个类型的默认值。派生 `Default` 实现了 `default` 函数。`default` 函数的派生实现调用了类型每部分的 `default` 函数,这意味着类型中所有的字段或值也必须实现了 `Default`,这样才能够派生 `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#使用结构体更新语法从其他实例创建实例 [creating-instances-from-other-instances-with-struct-update-syntax]: ch05-01-defining-structs.html#使用结构体更新语法创建实例
[stack-only-data-copy]: ch04-01-what-is-ownership.html#只在栈上的数据拷贝 [stack-only-data-copy]: ch04-01-what-is-ownership.html#只在栈上的数据拷贝
[variables-and-data-interacting-with-clone]: ch04-01-what-is-ownership.html#使用克隆的变量与数据交互 [variables-and-data-interacting-with-clone]: ch04-01-what-is-ownership.html#使用克隆的变量与数据交互
[macros]: ch20-05-macros.html#宏 [macros]: ch20-05-macros.html#宏

@ -182,4 +182,4 @@
[tuples]: ch03-02-data-types.html#元组类型 [tuples]: ch03-02-data-types.html#元组类型
[move]: ch04-01-what-is-ownership.html#使用移动的变量与数据交互 [move]: ch04-01-what-is-ownership.html#使用移动的变量与数据交互
[copy]: ch04-01-what-is-ownership.html#只在栈上的数据-拷贝 [copy]: ch04-01-what-is-ownership.html#只在栈上的数据拷贝

@ -1,7 +1,6 @@
## `match` 控制流结构 ## `match` 控制流结构
<!-- https://github.com/rust-lang/book/blob/main/src/ch06-02-match.md --> [ch06-02-match.md](https://github.com/rust-lang/book/blob/602a0d2e898f5e4ff030eac0b457755a10e0be1e/src/ch06-02-match.md)
<!-- commit 5d22a358fb2380aa3f270d7b6269b67b8e44849e -->
Rust 有一个叫做 `match` 的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码。模式可由字面值、变量、通配符和许多其他内容构成;[第十九章][ch19-00-patterns]会涉及到所有不同种类的模式以及它们的作用。`match` 的力量来源于模式的表现力,以及编译器能够确认所有可能情况均已被覆盖。 Rust 有一个叫做 `match` 的极为强大的控制流运算符,它允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应代码。模式可由字面值、变量、通配符和许多其他内容构成;[第十九章][ch19-00-patterns]会涉及到所有不同种类的模式以及它们的作用。`match` 的力量来源于模式的表现力,以及编译器能够确认所有可能情况均已被覆盖。

@ -1,7 +1,6 @@
## `if let``let else` 简洁控制流 ## `if let``let else` 简洁控制流
<!-- https://github.com/rust-lang/book/blob/main/src/ch06-03-if-let.md --> [ch06-03-if-let.md](https://github.com/rust-lang/book/blob/f78ab89d7545ac17780e6a367055cc089f4cd2ec/src/ch06-03-if-let.md)
<!-- commit 746bf4c43ef0a2810b1f2cb234bfadd5ca5382f6 -->
`if let` 语法让我们以一种不那么冗长的方式结合 `if``let`,来处理只匹配一个模式的值而忽略其他模式的情况。考虑示例 6-6 中的程序,它匹配一个 `config_max` 变量中的 `Option<u8>` 值并只希望当值为 `Some` 变体时执行代码: `if let` 语法让我们以一种不那么冗长的方式结合 `if``let`,来处理只匹配一个模式的值而忽略其他模式的情况。考虑示例 6-6 中的程序,它匹配一个 `config_max` 变量中的 `Option<u8>` 值并只希望当值为 `Some` 变体时执行代码:
@ -21,7 +20,7 @@
`if let` 语法获取通过等号分隔的一个模式和一个表达式。它的工作方式与 `match` 相同,这里的表达式对应 `match` 而模式则对应第一个分支。在这个例子中,模式是 `Some(max)``max` 绑定为 `Some` 中的值。接着可以在 `if let` 代码块中使用 `max` 了,就跟在对应的 `match` 分支中一样。只有当值匹配该模式时,`if let` 块中的代码才会执行。 `if let` 语法获取通过等号分隔的一个模式和一个表达式。它的工作方式与 `match` 相同,这里的表达式对应 `match` 而模式则对应第一个分支。在这个例子中,模式是 `Some(max)``max` 绑定为 `Some` 中的值。接着可以在 `if let` 代码块中使用 `max` 了,就跟在对应的 `match` 分支中一样。只有当值匹配该模式时,`if let` 块中的代码才会执行。
使用 `if let` 意味着编写更少代码,更少的缩进和更少的样板代码。然而,这样会失去 `match` 强制要求的穷尽性检查来确保你没有忘记处理某些情况。`match` 和 `if let` 之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。 使用 `if let` 意味着更少的输入、更少的缩进,也更少的样板代码。然而,这样也会失去 `match` 所强制的穷尽性检查,也就无法确保你没有遗漏某些情况。`match` 和 `if let` 之间的选择依赖特定的环境以及增加简洁度和失去穷尽性检查的权衡取舍。
换句话说,可以认为 `if let``match` 的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。 换句话说,可以认为 `if let``match` 的一个语法糖,它当值匹配某一模式时执行代码而忽略所有其他值。
@ -85,7 +84,7 @@
</figure> </figure>
注意它以这种方式在函数主体中保持了 “愉快路径”“Happy Path”而不用像 `if let` 那样在两个分支中拥有明显不同的控制流 注意这种写法能让函数主体沿着“愉快路径”“Happy Path”继续向下而不必像 `if let` 那样让两个分支具有明显不同的控制流。
如果你的程序遇到一个使用 `match` 表达起来过于冗长的逻辑,记住 `if let``let...else` 也在你的 Rust 工具箱中。 如果你的程序遇到一个使用 `match` 表达起来过于冗长的逻辑,记住 `if let``let...else` 也在你的 Rust 工具箱中。

@ -1,7 +1,6 @@
# 使用包、Crate 和模块管理不断增长的项目 # 包、Crates 与模块
<!-- https://github.com/rust-lang/book/blob/main/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md --> [ch07-00-managing-growing-projects-with-packages-crates-and-modules.md](https://github.com/rust-lang/book/blob/79b9d15410a7b15a65cb86bcb40cbea99198a9e4/src/ch07-00-managing-growing-projects-with-packages-crates-and-modules.md)
<!-- commit 5d22a358fb2380aa3f270d7b6269b67b8e44849e -->
当你编写大型程序时,组织代码显得尤为重要。通过对相关功能进行分组和划分不同功能的代码,你可以清楚在哪里可以找到实现了特定功能的代码,以及在哪里可以改变一个功能的工作方式。 当你编写大型程序时,组织代码显得尤为重要。通过对相关功能进行分组和划分不同功能的代码,你可以清楚在哪里可以找到实现了特定功能的代码,以及在哪里可以改变一个功能的工作方式。
@ -14,7 +13,7 @@
Rust 有许多功能可以让你管理代码的组织,包括哪些细节可以被公开,哪些细节作为私有部分,以及程序中各个作用域中有哪些名称。这些特性,有时被统称为 “模块系统the module system包括 Rust 有许多功能可以让你管理代码的组织,包括哪些细节可以被公开,哪些细节作为私有部分,以及程序中各个作用域中有哪些名称。这些特性,有时被统称为 “模块系统the module system包括
- **包***Packages*Cargo 的一个功能,它允许你构建、测试和分享 crate。 - **包***Packages*Cargo 的一个功能,它允许你构建、测试和分享 crate。
- **Crates** :一个模块的树形结构,它形成了库或可执行文件项目 - **Crates**:一个模块树,可以产生一个库或可执行文件
- **模块***Modules*)和 **use**:允许你控制作用域和路径的私有性。 - **模块***Modules*)和 **use**:允许你控制作用域和路径的私有性。
- **路径***path*):一个为例如结构体、函数或模块等项命名的方式。 - **路径***path*):一个为例如结构体、函数或模块等项命名的方式。

@ -1,11 +1,10 @@
## 包和 Crate ## 包和 Crate
<!-- https://github.com/rust-lang/book/blob/main/src/ch07-01-packages-and-crates.md --> [ch07-01-packages-and-crates.md](https://github.com/rust-lang/book/blob/79b9d15410a7b15a65cb86bcb40cbea99198a9e4/src/ch07-01-packages-and-crates.md)
<!-- commit 02e053cdbbb3bf9edd9ad32ed49eb533404350a9 -->
模块系统的第一部分,我们将介绍包和 crate。 模块系统的第一部分,我们将介绍包和 crate。
crate 是 Rust 在编译时最小的代码单位。即使你用 `rustc` 而不是 `cargo` 来编译一个单独的源代码文件(正如我们在第 1 章“编写并运行 Rust 程序”中所做的那样),编译器还是会将那个文件视为一个 crate。crate 可以包含模块,模块可以定义在其他文件,然后和 crate 一起编译,我们会在接下来的章节中遇到 crate 是 Rust 编译器每次处理的最小代码单位。即使你用 `rustc` 而不是 `cargo` 来编译单个源代码文件(正如我们在第一章的 [“Rust 程序基础”][basics] 中做的那样),编译器也会把那个文件视为一个 crate。crate 可以包含模块,而这些模块也可以定义在其他文件中,并与该 crate 一起编译;我们会在接下来的小节中看到这一点
crate 有两种形式:二进制 crate 和库 crate。**二进制 crate***Binary crates*)可以被编译为可执行程序,比如命令行程序或者服务端。它们必须有一个名为 `main` 函数来定义当程序被执行的时候所需要做的事情。目前我们所创建的 crate 都是二进制 crate。 crate 有两种形式:二进制 crate 和库 crate。**二进制 crate***Binary crates*)可以被编译为可执行程序,比如命令行程序或者服务端。它们必须有一个名为 `main` 函数来定义当程序被执行的时候所需要做的事情。目前我们所创建的 crate 都是二进制 crate。
@ -33,5 +32,6 @@ main.rs
在此,我们有了一个只包含 *src/main.rs* 的包,意味着它只含有一个名为 `my-project` 的二进制 crate。如果一个包同时含有 *src/main.rs**src/lib.rs*,则它有两个 crate一个二进制的和一个库的且名字都与包相同。通过将文件放在 *src/bin* 目录下,一个包可以拥有多个二进制 crate每个 *src/bin* 下的文件都会被编译成一个独立的二进制 crate。 在此,我们有了一个只包含 *src/main.rs* 的包,意味着它只含有一个名为 `my-project` 的二进制 crate。如果一个包同时含有 *src/main.rs**src/lib.rs*,则它有两个 crate一个二进制的和一个库的且名字都与包相同。通过将文件放在 *src/bin* 目录下,一个包可以拥有多个二进制 crate每个 *src/bin* 下的文件都会被编译成一个独立的二进制 crate。
[basics]: ch01-02-hello-world.html#rust-程序基础
[modules]: ch07-02-defining-modules-to-control-scope-and-privacy.html [modules]: ch07-02-defining-modules-to-control-scope-and-privacy.html
[rand]: ch02-00-guessing-game-tutorial.html#生成一个随机数 [rand]: ch02-00-guessing-game-tutorial.html#生成一个随机数

@ -1,7 +1,6 @@
## 定义模块来控制作用域与私有性 ## 定义模块来控制作用域与私有性
<!-- https://github.com/rust-lang/book/blob/main/src/ch07-02-defining-modules-to-control-scope-and-privacy.md --> [ch07-02-defining-modules-to-control-scope-and-privacy.md](https://github.com/rust-lang/book/blob/8c0eacd5c4acbb650497454f3a58c9e8083202a4/src/ch07-02-defining-modules-to-control-scope-and-privacy.md)
<!-- commit 3a30e4c1fbe641afc066b3af9eb01dcdf5ed8b24 -->
在本节,我们将讨论模块和其它一些关于模块系统的部分,如允许你命名项的 *路径**paths*);用来将路径引入作用域的 `use` 关键字;以及使项变为公有的 `pub` 关键字。我们还将讨论 `as` 关键字、外部包external packages和 glob 运算符glob operator 在本节,我们将讨论模块和其它一些关于模块系统的部分,如允许你命名项的 *路径**paths*);用来将路径引入作用域的 `use` 关键字;以及使项变为公有的 `pub` 关键字。我们还将讨论 `as` 关键字、外部包external packages和 glob 运算符glob operator

@ -1,7 +1,6 @@
## 引用模块树中项的路径 ## 引用模块树中项的路径
<!-- https://github.com/rust-lang/book/blob/main/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md --> [ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md](https://github.com/rust-lang/book/blob/79b9d15410a7b15a65cb86bcb40cbea99198a9e4/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.md)
<!-- commit 5d22a358fb2380aa3f270d7b6269b67b8e44849e -->
为了向 Rust 指示在模块树中从何处查找某个项,我们使用路径,就像在文件系统中使用路径一样。为了调用一个函数,我们需要知道它的路径。 为了向 Rust 指示在模块树中从何处查找某个项,我们使用路径,就像在文件系统中使用路径一样。为了调用一个函数,我们需要知道它的路径。

@ -1,7 +1,6 @@
## 使用 `use` 关键字将路径引入作用域 ## 使用 `use` 关键字将路径引入作用域
<!-- https://github.com/rust-lang/book/blob/main/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md --> [ch07-04-bringing-paths-into-scope-with-the-use-keyword.md](https://github.com/rust-lang/book/blob/79b9d15410a7b15a65cb86bcb40cbea99198a9e4/src/ch07-04-bringing-paths-into-scope-with-the-use-keyword.md)
<!-- commit 72ad14e4acb12438aa467c4cf256e0bc55df585a -->
不得不编写路径来调用函数显得繁琐且重复。在示例 7-7 中,无论我们选择 `add_to_waitlist` 函数的绝对路径还是相对路径,每次我们想要调用 `add_to_waitlist` 时,都必须指定`front_of_house` 和 `hosting`。幸运的是,有一种方法可以简化这个过程。我们可以使用 `use` 关键字创建一个捷径,然后就可以在作用域中的任何地方使用这个更短的名字。 不得不编写路径来调用函数显得繁琐且重复。在示例 7-7 中,无论我们选择 `add_to_waitlist` 函数的绝对路径还是相对路径,每次我们想要调用 `add_to_waitlist` 时,都必须指定`front_of_house` 和 `hosting`。幸运的是,有一种方法可以简化这个过程。我们可以使用 `use` 关键字创建一个捷径,然后就可以在作用域中的任何地方使用这个更短的名字。
@ -177,7 +176,7 @@ use std::collections::HashMap;
这一行便将 `std::io``std::io::Write` 同时引入作用域。 这一行便将 `std::io``std::io::Write` 同时引入作用域。
### glob 运算符 ### 通过 glob 运算符导入项
如果希望将一个路径下**所有**公有项引入作用域,可以指定路径后跟 `*` glob 运算符: 如果希望将一个路径下**所有**公有项引入作用域,可以指定路径后跟 `*` glob 运算符:
@ -187,7 +186,7 @@ use std::collections::*;
这个 `use` 语句将 `std::collections` 中定义的所有公有项引入当前作用域。使用 glob 运算符时请多加小心Glob 会使得我们难以推导作用域中有什么名称和它们是在何处定义的。 这个 `use` 语句将 `std::collections` 中定义的所有公有项引入当前作用域。使用 glob 运算符时请多加小心Glob 会使得我们难以推导作用域中有什么名称和它们是在何处定义的。
glob 运算符经常用于测试模块 `tests`这时会将所有内容引入作用域我们将在第十一章“如何编写测试”部分讲解。glob 运算符有时也用于 prelude 模式;查看[标准库中的文档](https://doc.rust-lang.org/std/prelude/index.html#other-preludes)了解这个模式的更多细节。 glob 运算符经常用于测试模块 `tests` 中,这时会将所有内容引入作用域;我们将在第十一章[“如何编写测试”][writing-tests]部分讲解。glob 运算符有时也用于 prelude 模式;查看[标准库中的文档](https://doc.rust-lang.org/std/prelude/index.html#other-preludes)了解这个模式的更多细节。
[ch14-pub-use]: ch14-02-publishing-to-crates-io.html#使用-pub-use-导出便捷的公有-api [ch14-pub-use]: ch14-02-publishing-to-crates-io.html#使用-pub-use-导出便捷的公有-api
[rand]: ch02-00-guessing-game-tutorial.html#生成一个随机数 [rand]: ch02-00-guessing-game-tutorial.html#生成一个随机数

@ -1,13 +1,12 @@
## 将模块拆分成多个文件 ## 将模块拆分成多个文件
<!-- https://github.com/rust-lang/book/blob/main/src/ch07-05-separating-modules-into-different-files.md --> [ch07-05-separating-modules-into-different-files.md](https://github.com/rust-lang/book/blob/79b9d15410a7b15a65cb86bcb40cbea99198a9e4/src/ch07-05-separating-modules-into-different-files.md)
<!-- commit 3a30e4c1fbe641afc066b3af9eb01dcdf5ed8b24 -->
到目前为止,本章所有的例子都在一个文件中定义多个模块。当模块变得更大时,你可能想要将它们的定义移动到单独的文件中,从而使代码更容易阅读 到目前为止,本章的所有示例都在一个文件中定义了多个模块。当模块变大时,你可能想把它们的定义移到单独的文件中,以便让代码更容易浏览
例如,我们从示例 7-17 中包含多个餐厅模块的代码开始。我们会将模块提取到各自的文件中,而不是将所有模块都定义到 crate 根文件中。在这里crate 根文件是 *src/lib.rs*,不过这个过程也适用于 crate 根文件是 *src/main.rs* 的二进制 crate。 例如,我们从示例 7-17 中包含多个餐厅模块的代码开始。我们会将模块提取到各自的文件中,而不是将所有模块都定义到 crate 根文件中。在这里crate 根文件是 *src/lib.rs*,不过这个过程也适用于 crate 根文件是 *src/main.rs* 的二进制 crate。
首先`front_of_house` 模块提取到其自己的文件中。删除 `front_of_house` 模块的大括号中的代码,只留下 `mod front_of_house;` 声明,这样 *src/lib.rs* 会包含如示例 7-21 所示的代码。注意直到创建示例 7-22 中的 *src/front_of_house.rs* 文件之前代码都不能编译。 首先`front_of_house` 模块提取到它自己的文件中。删除 `front_of_house` 模块花括号内的代码,只保留 `mod front_of_house;` 声明,这样 *src/lib.rs* 就会只剩下示例 7-21 所示的代码。注意,在创建出示例 7-22 中的 *src/front_of_house.rs* 文件之前,这段代码都无法编译。
<span class="filename">文件名src/lib.rs</span> <span class="filename">文件名src/lib.rs</span>
@ -17,7 +16,7 @@
<span class="caption">示例 7-21: 声明 `front_of_house` 模块,其内容将位于 *src/front_of_house.rs*</span> <span class="caption">示例 7-21: 声明 `front_of_house` 模块,其内容将位于 *src/front_of_house.rs*</span>
接下来将之前大括号内的代码放入一个名叫 *src/front_of_house.rs* 的新文件中,如示例 7-22 所示。因为编译器找到了 crate 根中名叫 `front_of_house` 的模块声明,它就知道去搜寻这个文件 接下来,把刚才花括号中的代码放进一个名为 *src/front_of_house.rs* 的新文件中,如示例 7-22 所示。编译器之所以知道要去这个文件里查找,是因为它在 crate 根中看到了名为 `front_of_house` 的模块声明
<span class="filename">文件名src/front_of_house.rs</span> <span class="filename">文件名src/front_of_house.rs</span>
@ -27,11 +26,11 @@
<span class="caption">示例 7-22: 在 *src/front_of_house.rs* 中定义 `front_of_house` 模块</span> <span class="caption">示例 7-22: 在 *src/front_of_house.rs* 中定义 `front_of_house` 模块</span>
注意你只需在模块树中的某处使用一次 `mod` 声明就可以加载这个文件。一旦编译器知道了这个文件是项目的一部分(并且通过 `mod` 语句的位置知道了代码在模块树中的位置),项目中的其他文件应该使用其所声明的位置的路径来引用那个文件的代码,这在[“引用模块项目的路径”][paths]部分有讲到。换句话说,`mod` **不是**你可能会在其他编程语言中看到的 “include” 操作。 注意,在模块树中,你只需要用 `mod` 声明加载某个文件一次。一旦编译器知道这个文件是项目的一部分了,并且因为 `mod` 语句所在的位置而知道这段代码位于模块树的什么位置,那么项目中的其他文件就应该用它声明位置对应的路径来引用这段代码,这一点会在[“引用模块树中项的路径”][paths]部分讲到。换句话说,`mod` **不是**某些其他编程语言里那种 “include” 操作。
接下来我们同样将 `hosting` 模块提取到自己的文件中。这个过程会有所不同,因为 `hosting``front_of_house` 的子模块而不是根模块。我们将 `hosting` 的文件放在与模块树中它的父模块同名的目录中,在这里是 *src/front_of_house/*。 接下来,我们也把 `hosting` 模块提取到它自己的文件中。这个过程稍有不同,因为 `hosting``front_of_house` 的子模块,而不是根模块。我们会把 `hosting` 对应的文件放进一个以其在模块树中的祖先模块命名的新目录中,这里就是 *src/front_of_house*。
为了移动 `hosting`,修改 *src/front_of_house.rs* 使之仅包含 `hosting` 模块的声明。 要开始移动 `hosting`,先把 *src/front_of_house.rs* 改成只包含 `hosting` 模块的声明:
<span class="filename">文件名src/front_of_house.rs</span> <span class="filename">文件名src/front_of_house.rs</span>
@ -39,43 +38,43 @@
{{#rustdoc_include ../listings/ch07-managing-growing-projects/no-listing-02-extracting-hosting/src/front_of_house.rs}} {{#rustdoc_include ../listings/ch07-managing-growing-projects/no-listing-02-extracting-hosting/src/front_of_house.rs}}
``` ```
接着我们创建一个 *src/front_of_house* 目录和一个包含 `hosting` 模块定义的 *hosting.rs* 文件 然后,创建一个 *src/front_of_house* 目录和一个 *hosting.rs* 文件,用来放置 `hosting` 模块中的定义
<span class="filename">文件名src/front_of_house/hosting.rs</span> <span class="filename">文件名src/front_of_house/hosting.rs</span>
```rust ```rust,ignore
{{#rustdoc_include ../listings/ch07-managing-growing-projects/no-listing-02-extracting-hosting/src/front_of_house/hosting.rs}} {{#rustdoc_include ../listings/ch07-managing-growing-projects/no-listing-02-extracting-hosting/src/front_of_house/hosting.rs}}
``` ```
如果*hosting.rs* 放在 *src* 目录,编译器会认为 `hosting` 模块中的 *hosting.rs* 的代码声明于 crate 根,而不是声明为 `front_of_house` 的子模块。编译器所遵循的哪些文件对应哪些模块的代码的规则,意味着目录和文件更紧密地贴合模块树 如果我们反而把 *hosting.rs* 放在 *src* 目录中,编译器就会认为 *hosting.rs* 里的代码属于在 crate 根中声明的 `hosting` 模块,而不是 `front_of_house` 的子模块。编译器关于“要去哪些文件中查找哪些模块代码”的规则,使得目录和文件结构会更贴近模块树本身
> ### 另一种文件路径 > ### 替代文件路径
> >
> 目前为止我们介绍了 Rust 编译器所最常用的文件路径,但 Rust 也支持一种更老的路径风格。 > 到目前为止,我们介绍的是 Rust 编译器最惯用的文件路径,不过 Rust 也支持一种较旧的文件路径风格。
> >
> 对于声明于 crate 根的 `front_of_house` 模块,编译器会在如下位置查找模块代码: > 对于在 crate 根中声明的 `front_of_house` 模块,编译器会在以下位置查找模块代码:
> >
> * *src/front_of_house.rs*(我们所介绍的 > - *src/front_of_house.rs*(前面介绍的方式
> * *src/front_of_house/mod.rs*(老风格,不过仍然支持) > - *src/front_of_house/mod.rs*(较旧的风格,但仍然受支持)
> >
> 对于 `front_of_house` 的子模块 `hosting`,编译器会在下位置查找模块代码: > 对于 `front_of_house` 的子模块 `hosting`,编译器会在下位置查找模块代码:
> >
> * *src/front_of_house/hosting.rs*(我们所介绍的 > - *src/front_of_house/hosting.rs*(前面介绍的方式
> * *src/front_of_house/hosting/mod.rs*(老风格,不过仍然支持) > - *src/front_of_house/hosting/mod.rs*(较旧的风格,但仍然受支持)
> >
> 如果你对同一模块同时使用这两种路径风格,会得到一个编译错误。在同一项目中的不同模块混用不同的路径风格是允许的,不过这会使他人感到疑惑。 > 如果你对同一个模块同时使用这两种风格,编译器就会报错。在同一个项目里为不同模块混用两种风格是允许的,不过这可能会让浏览项目的人感到困惑。
> >
> 使用 *mod.rs*一文件名的风格的主要缺点是会导致项目中出现很多 *mod.rs* 文件,当你在编辑器中同时打开它们时会感到疑惑 > 使用 *mod.rs*种文件名风格的主要缺点是,项目里最后可能会有很多都叫 *mod.rs* 的文件;当你在编辑器里同时打开它们时,就会变得很混乱
我们将各个模块的代码移动到独立文件了,同时模块树保持不变。`eat_at_restaurant` 中的函数调用也无需修改继续保持有效,即便其定义存在于不同的文件中。这个技巧让你可以在模块代码增长时,将它们移动到新文件中 现在,我们已经把每个模块的代码都移动到了独立文件中,而模块树保持不变。`eat_at_restaurant` 中的函数调用也完全不需要修改,即使这些定义现在位于不同的文件中也一样能工作。这个技巧让你可以在模块逐渐变大时,再把它们迁移到新文件里
注意,*src/lib.rs* 中的 `pub use crate::front_of_house::hosting` 语句也并未发生改变,`use` 也不会对哪些文件会被编译为 crate 的一部分有任何影响。`mod` 关键字声明了模块,而 Rust 会在与模块同名的文件中查找模块的代码。 注意,*src/lib.rs* 中的 `pub use crate::front_of_house::hosting` 语句也完全没有变化,而且 `use` 也不会影响 crate 会编译哪些文件。`mod` 关键字用来声明模块,而 Rust 会在与模块同名的文件中查找应放进该模块的代码。
## 总结 ## 总结
Rust 允许你将一个包拆分为多个 crate并将一个 crate 拆分为若干模块,从而可以在一个模块中引用另一个模块中定义的项。你可以使用绝对路径或相对路径来实现这一点。你可以通过使用 `use` 语句将路径引入作用域,这样在多次使用时可以使用更短的路径。模块定义的代码默认是私有的,不过可以选择增加 `pub` 关键字使其定义变为公有 Rust 允许你把一个包拆分成多个 crate再把一个 crate 拆分成多个模块,这样你就能在一个模块中引用另一个模块里定义的项。你既可以使用绝对路径,也可以使用相对路径。还可以用 `use` 语句把路径引入作用域,这样在同一作用域内多次使用该项时就能写更短的路径。模块代码默认是私有的,不过你可以加上 `pub` 关键字,把定义公开出去
接下来,让我们看看一些标准库提供的集合数据类型,你可以利用它们编写出漂亮整洁的代码 下一章,我们将看看标准库中的一些集合数据结构,你可以在组织良好的代码中使用它们
[paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html [paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html

@ -193,7 +193,7 @@ fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {
### 使用 trait bound 有条件地实现方法 ### 使用 trait bound 有条件地实现方法
通过使用带有 trait bound 的泛型参数的 `impl` 块,可以有条件地只为那些实现了特定 trait 的类型实现方法。例如,示例 10-15 中的类型 `Pair<T>` 总是实现了 `new` 方法并返回一个 `Pair<T>` 的实例(回忆一下第五章的 [“定义方法”][methods] 部分,`Self` 是一个 `impl` 块类型的类型别名type alias在这里是 `Pair<T>`)。不过在下一个 `impl` 块中,只有那些为 `T` 类型实现了 `PartialOrd` trait来允许比较 **和** `Display` trait来启用打印`Pair<T>` 才会实现 `cmp_display` 方法: 通过使用带有 trait bound 的泛型参数的 `impl` 块,可以有条件地只为那些实现了特定 trait 的类型实现方法。例如,示例 10-15 中的类型 `Pair<T>` 总是实现了 `new` 方法并返回一个 `Pair<T>` 的实例(回忆一下第五章的 [“方法语法”][methods] 部分,`Self` 是一个 `impl` 块类型的类型别名type alias在这里是 `Pair<T>`)。不过在下一个 `impl` 块中,只有那些为 `T` 类型实现了 `PartialOrd` trait来允许比较 **和** `Display` trait来启用打印`Pair<T>` 才会实现 `cmp_display` 方法:
```rust,noplayground ```rust,noplayground
{{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-15/src/lib.rs}} {{#rustdoc_include ../listings/ch10-generic-types-traits-and-lifetimes/listing-10-15/src/lib.rs}}
@ -221,4 +221,4 @@ Trait 和 trait bound 让我们能够使用泛型类型参数来减少重复,
[using-trait-objects-that-allow-for-values-of-different-types]: [using-trait-objects-that-allow-for-values-of-different-types]:
ch18-02-trait-objects.html#顾及不同类型值的-trait-对象 ch18-02-trait-objects.html#顾及不同类型值的-trait-对象
[methods]: ch05-03-method-syntax.html#定义方法 [methods]: ch05-03-method-syntax.html#方法语法

@ -124,7 +124,7 @@ adder
└── integration_test.rs └── integration_test.rs
``` ```
这是一种老的命名规范,正如第七章[“另一种文件路径”][alt-paths]中提到的 Rust 仍然理解它们。这样命名告诉 Rust 不要将 `common` 看作一个集成测试文件。将 `setup` 函数代码移动到 *tests/common/mod.rs* 并删除 *tests/common.rs* 文件之后,测试输出中将不会出现这一部分。*tests* 目录中的子目录不会被作为单独的 crate 编译或作为一个测试结果部分出现在测试输出中。 这是一种老的命名规范,正如第七章[“替代文件路径”][alt-paths]中提到的 Rust 仍然理解它们。这样命名告诉 Rust 不要将 `common` 看作一个集成测试文件。将 `setup` 函数代码移动到 *tests/common/mod.rs* 并删除 *tests/common.rs* 文件之后,测试输出中将不会出现这一部分。*tests* 目录中的子目录不会被作为单独的 crate 编译或作为一个测试结果部分出现在测试输出中。
一旦创建了 *tests/common/mod.rs*,就可以将其作为模块以便在任何集成测试文件中使用。这里是一个 *tests/integration_test.rs* 中调用 `setup` 函数的 `it_adds_two` 测试的示例: 一旦创建了 *tests/common/mod.rs*,就可以将其作为模块以便在任何集成测试文件中使用。这里是一个 *tests/integration_test.rs* 中调用 `setup` 函数的 `it_adds_two` 测试的示例:
@ -151,4 +151,4 @@ Rust 的测试功能提供了一个确保即使你改变了函数的实现方式
[paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html [paths]: ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html
[separating-modules-into-files]: [separating-modules-into-files]:
ch07-05-separating-modules-into-different-files.html ch07-05-separating-modules-into-different-files.html
[alt-paths]: ch07-05-separating-modules-into-different-files.html#另一种文件路径 [alt-paths]: ch07-05-separating-modules-into-different-files.html#替代文件路径

@ -93,7 +93,7 @@
`type Target = T;` 语法定义了用于此 trait 的关联类型。关联类型是一个稍有不同的定义泛型参数的方式,现在还无需过多地担心它;第二十章会详细介绍。 `type Target = T;` 语法定义了用于此 trait 的关联类型。关联类型是一个稍有不同的定义泛型参数的方式,现在还无需过多地担心它;第二十章会详细介绍。
`deref` 方法体中写入了 `&self.0`,这样 `deref` 返回了我希望通过 `*` 运算符访问的值的引用。回忆一下第五章 [“使用没有命名字段的元组结构体创建不同的类型”][tuple-structs] 部分 `.0` 用来访问元组结构体的第一个元素。示例 15-9 中的 `main` 函数中对 `MyBox<T>` 值的 `*` 调用现在可以编译并能通过断言了! `deref` 方法体中写入了 `&self.0`,这样 `deref` 返回了我希望通过 `*` 运算符访问的值的引用。回忆一下第五章 [“使用元组结构体创建不同的类型”][tuple-structs] 部分 `.0` 用来访问元组结构体的第一个元素。示例 15-9 中的 `main` 函数中对 `MyBox<T>` 值的 `*` 调用现在可以编译并能通过断言了!
没有 `Deref` trait 的话,编译器只会解引用 `&` 引用类型。`deref` 方法向编译器提供了获取任何实现了 `Deref` trait 的类型的值,并且调用这个类型的 `deref` 方法来获取一个它知道如何解引用的 `&` 引用的能力。 没有 `Deref` trait 的话,编译器只会解引用 `&` 引用类型。`deref` 方法向编译器提供了获取任何实现了 `Deref` trait 的类型的值,并且调用这个类型的 `deref` 方法来获取一个它知道如何解引用的 `&` 引用的能力。
@ -166,4 +166,4 @@ Rust 在发现类型和 trait 实现满足三种情况时会进行 Deref 强制
第三个情况有些微妙Rust 也会将可变引用强转为不可变引用。但反之是**不可能** 的不可变引用永远也不能强转为可变引用。因为根据借用规则如果有一个可变引用其必须是这些数据的唯一引用否则程序将无法编译。将一个可变引用转换为不可变引用永远也不会打破借用规则。将不可变引用转换为可变引用则需要初始的不可变引用是数据唯一的不可变引用而借用规则无法保证这一点。因此Rust 无法假设将不可变引用转换为可变引用是可能的。 第三个情况有些微妙Rust 也会将可变引用强转为不可变引用。但反之是**不可能** 的不可变引用永远也不能强转为可变引用。因为根据借用规则如果有一个可变引用其必须是这些数据的唯一引用否则程序将无法编译。将一个可变引用转换为不可变引用永远也不会打破借用规则。将不可变引用转换为可变引用则需要初始的不可变引用是数据唯一的不可变引用而借用规则无法保证这一点。因此Rust 无法假设将不可变引用转换为可变引用是可能的。
[impl-trait]: ch10-02-traits.html#为类型实现-trait [impl-trait]: ch10-02-traits.html#为类型实现-trait
[tuple-structs]: ch05-01-defining-structs.html#使用没有命名字段的元组结构体创建不同的类型 [tuple-structs]: ch05-01-defining-structs.html#使用元组结构体创建不同的类型

@ -252,7 +252,7 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
### 使用 newtype 模式在外部类型上实现外部 trait ### 使用 newtype 模式在外部类型上实现外部 trait
在第十章的 [“为类型实现 trait”][implementing-a-trait-on-a-type] 部分我们提到了孤儿规则orphan rule它规定只有当 trait 或类型至少有一方或两者都对于当前 crate 是本地时,才能在该类型上实现该 trait。一个绕开这个限制的方法是使用 **newtype 模式***newtype pattern*),它涉及到在一个元组结构体(第五章 [“用没有命名字段的元组结构体创建不同的类型”][tuple-structs] 部分介绍了元组结构体)中创建一个新类型。这个元组结构体带有一个字段作为希望实现 trait 的类型的简单封装。由于这个封装类型对于 crate 是本地的,这样就可以在这个封装上实现 trait。*Newtype* 是一个源自 Haskell 编程语言的概念。使用这个模式没有运行时性能惩罚,这个封装类型在编译时就被省略了。 在第十章的 [“为类型实现 trait”][implementing-a-trait-on-a-type] 部分我们提到了孤儿规则orphan rule它规定只有当 trait 或类型至少有一方或两者都对于当前 crate 是本地时,才能在该类型上实现该 trait。一个绕开这个限制的方法是使用 **newtype 模式***newtype pattern*),它涉及到在一个元组结构体(第五章 [“使用元组结构体创建不同的类型”][tuple-structs] 部分介绍了元组结构体)中创建一个新类型。这个元组结构体带有一个字段作为希望实现 trait 的类型的简单封装。由于这个封装类型对于 crate 是本地的,这样就可以在这个封装上实现 trait。*Newtype* 是一个源自 Haskell 编程语言的概念。使用这个模式没有运行时性能惩罚,这个封装类型在编译时就被省略了。
例如,如果想要在 `Vec<T>` 上实现 `Display`,而孤儿规则阻止我们直接这么做,因为 `Display` trait 和 `Vec<T>` 都定义于我们的 crate 之外。可以创建一个包含 `Vec<T>` 实例的 `Wrapper` 结构体,接着可以如示例 20-24 那样在 `Wrapper` 上实现 `Display` 并使用 `Vec<T>` 的值: 例如,如果想要在 `Vec<T>` 上实现 `Display`,而孤儿规则阻止我们直接这么做,因为 `Display` trait 和 `Vec<T>` 都定义于我们的 crate 之外。可以创建一个包含 `Vec<T>` 实例的 `Wrapper` 结构体,接着可以如示例 20-24 那样在 `Wrapper` 上实现 `Display` 并使用 `Vec<T>` 的值:
@ -274,4 +274,4 @@ Rust 既不能避免一个 trait 与另一个 trait 拥有相同名称的方法
[implementing-a-trait-on-a-type]: ch10-02-traits.html#为类型实现-trait [implementing-a-trait-on-a-type]: ch10-02-traits.html#为类型实现-trait
[traits-defining-shared-behavior]: ch10-02-traits.html#trait定义共同行为 [traits-defining-shared-behavior]: ch10-02-traits.html#trait定义共同行为
[smart-pointer-deref]: ch15-02-deref.html#使用-deref-trait-将智能指针当作常规引用处理 [smart-pointer-deref]: ch15-02-deref.html#使用-deref-trait-将智能指针当作常规引用处理
[tuple-structs]: ch05-01-defining-structs.html#使用没有命名字段的元组结构体创建不同的类型 [tuple-structs]: ch05-01-defining-structs.html#使用元组结构体创建不同的类型

Loading…
Cancel
Save