Merge pull request #356 from shengurun/master

第七章翻译风格校正
pull/358/head
KaiserY 5 years ago committed by GitHub
commit 9be3dda54e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -6,13 +6,13 @@
当你编写大型程序时,组织你的代码显得尤为重要,因为你想在脑海中通晓整个程序,那几乎是不可能完成的。通过对相关功能进行分组和划分不同功能的代码,你可以清楚在哪里可以找到实现了特定功能的代码,以及在哪里可以改变一个功能的工作方式。
到目前为止,我们编写的程序都在一个文件的一个模块中。伴随着项目的增长,你可以通过将代码分解为多个模块和多个文件来组织代码。一个包可以包含多个二进制 crate 项和一个可选的 crate 库。伴随着包的增长,你可以将包中的部分代码提取出来,做成独立的 crate ,这些 crate 则作为外部依赖项。本章将会涵盖所有这些概念。对于一个由一系列相互关联的包组合而成的超大型项目, Cargo 提供了“工作空间”这一功能,我们将在第十四章的“[Cargo Workspaces](https://github.com/rust-lang/book/blob/master/src/ch14-03-cargo-workspaces.html)”对此进行讲解。
到目前为止,我们编写的程序都在一个文件的一个模块中。伴随着项目的增长,你可以通过将代码分解为多个模块和多个文件来组织代码。一个包可以包含多个二进制 crate 项和一个可选的 crate 库。伴随着包的增长,你可以将包中的部分代码提取出来,做成独立的 crate这些 crate 则作为外部依赖项。本章将会涵盖所有这些概念。对于一个由一系列相互关联的包组合而成的超大型项目Cargo 提供了 “工作空间” 这一功能,我们将在第十四章的 “[Cargo Workspaces](https://github.com/rust-lang/book/blob/master/src/ch14-03-cargo-workspaces.html)” 对此进行讲解。
除了对功能进行分组以外,封装实现细节可以使你更高级地重用代码:你实现了一个操作后,其他的代码可以通过该代码的公共接口来进行调用,而不需要知道它是如何实现的。你在编写代码时可以定义哪些部分是其他代码可以使用的公共部分,以及哪些部分是你有权更改实现细节的私有部分。这是另一种减少你在脑海中记住项目内容数量的方法。
这里有一个需要说明的概念“作用域scope代码所在的嵌套上下文有一组定义为 in scope ”的名称。当阅读、编写和编译代码时,程序员和编译器需要知道特定位置的特定名称是否引用了变量、函数、结构体、枚举、模块、常量或者其他有意义的项。你可以创建作用域,以及改变哪些名称在作用域内还是作用域外。同一个作用域内不能拥有两个相同名称的项;可以使用一些工具来解决名称冲突。
这里有一个需要说明的概念 “作用域scope代码所在的嵌套上下文有一组定义为 in scope” 的名称。当阅读、编写和编译代码时,程序员和编译器需要知道特定位置的特定名称是否引用了变量、函数、结构体、枚举、模块、常量或者其他有意义的项。你可以创建作用域,以及改变哪些名称在作用域内还是作用域外。同一个作用域内不能拥有两个相同名称的项;可以使用一些工具来解决名称冲突。
Rust 有许多功能可以让你管理代码的组织,包括哪些内容可以被公开,哪些内容作为私有部分,以及程序每个作用域中的名字。这些功能。这有时被称为 “模块系统the module system),包括:
Rust 有许多功能可以让你管理代码的组织,包括哪些内容可以被公开,哪些内容作为私有部分,以及程序每个作用域中的名字。这些功能。这有时被称为 “模块系统the module system,包括:
* **包***Packages* Cargo 的一个功能,它允许你构建、测试和分享 crate。
* **Crates** :一个模块的树形结构,它形成了库或二进制项目。

@ -4,11 +4,11 @@
> <br>
> commit 879fef2345bf32751a83a9e779e0cb84e79b6d3d
模块系统的第一部分,我们将介绍包和 crate crate 是一个二进制项或者库。*crate root* 是一个源文件Rust 编译器以它为起始点,并构成你的 crate 的根模块(我们将在“[Defining Modules to Control Scope and Privacy](https://github.com/rust-lang/book/blob/master/src/ch07-02-defining-modules-to-control-scope-and-privacy.md)”一节深入解读)。*包**package*)是提供一系列功能的一个或者多个 crate 。一个包会包含有一个*Cargo.toml*文件,阐述如何去构建这些 crate
模块系统的第一部分,我们将介绍包和 crate。crate 是一个二进制项或者库。*crate root* 是一个源文件Rust 编译器以它为起始点,并构成你的 crate 的根模块(我们将在 “[Defining Modules to Control Scope and Privacy](https://github.com/rust-lang/book/blob/master/src/ch07-02-defining-modules-to-control-scope-and-privacy.md)” 一节深入解读)。*包**package* 是提供一系列功能的一个或者多个 crate。一个包会包含有一个 *Cargo.toml* 文件,阐述如何去构建这些 crate。
包中所包含的内容由几条规则来确立。一个包中至多 **只能** 包含一个库 crate(library crate);包中可以包含任意多个二进制 crate(binary crate);包中至少包含一个 crate无论是库的还是二进制的。
让我们来看看创建包的时候会发生什么。首先,我们输入命令`cargo new`:
让我们来看看创建包的时候会发生什么。首先,我们输入命令 `cargo new`
```text
$ cargo new my-project
@ -20,12 +20,12 @@ $ ls my-project/src
main.rs
```
当我们输入了这条命令Cargo 会给我们的包创建一个*Cargo.toml*文件。查看 *Cargo.toml* 的内容,会发现并没有提到 *src/main.rs*,因为 Cargo 遵循的一个约定:*src/main.rs* 就是一个与包同名的二进制 crate 的 crate 根。同样的Cargo 知道如果包目录中包含 *src/lib.rs*,则包带有与其同名的库 crate*src/lib.rs* 是 crate 根。crate 根文件将由 Cargo 传递给 `rustc` 来实际构建库或者二进制项目。
当我们输入了这条命令Cargo 会给我们的包创建一个 *Cargo.toml* 文件。查看 *Cargo.toml* 的内容,会发现并没有提到 *src/main.rs*,因为 Cargo 遵循的一个约定:*src/main.rs* 就是一个与包同名的二进制 crate 的 crate 根。同样的Cargo 知道如果包目录中包含 *src/lib.rs*,则包带有与其同名的库 crate*src/lib.rs* 是 crate 根。crate 根文件将由 Cargo 传递给 `rustc` 来实际构建库或者二进制项目。
在此,我们有了一个只包含*src/main.rs*的包,意味着它只含有一个名为`my-project`的二进制 crate 。如果一个包同时含有*src/main.rs*和*src/lib.rs*,则它有两个 crate :一个库和一个二进制项,且名字都与包相同。通过将文件放在*src/bin*目录下,一个包可以享有多个二进制 crate :每个文件都是一个分离出来的二进制 crate
在此,我们有了一个只包含 *src/main.rs* 的包,意味着它只含有一个名为 `my-project` 的二进制 crate。如果一个包同时含有 *src/main.rs* *src/lib.rs*,则它有两个 crate一个库和一个二进制项且名字都与包相同。通过将文件放在 *src/bin* 目录下,一个包可以享有多个二进制 crate每个文件都是一个分离出来的二进制 crate。
一个 crate 会将一个作用域内的相关功能分组到一起,使得该功能可以很方便地在多个项目之间共享。举一个例子,我们在[第二章](https://github.com/rust-lang/book/blob/master/src/ch02-00-guessing-game-tutorial.md#generating-a-random-number)使用的`rand` crate 提供了生成随机数的功能。通过将`rand` crate 加入到我们项目的作用域中,我们就可以在自己的项目中使用该功能。`rand` crate 提供的所有功能都可以通过该 crate 的名字:`rand` 进行访问。
一个 crate 会将一个作用域内的相关功能分组到一起,使得该功能可以很方便地在多个项目之间共享。举一个例子,我们在 [第二章](https://github.com/rust-lang/book/blob/master/src/ch02-00-guessing-game-tutorial.md#generating-a-random-number) 使用的 `rand` crate 提供了生成随机数的功能。通过将 `rand` crate 加入到我们项目的作用域中,我们就可以在自己的项目中使用该功能。`rand` crate 提供的所有功能都可以通过该 crate 的名字:`rand` 进行访问。
将一个 crate 的功能保持在其自身的作用域中,可以知晓一些特定的功能是在我们的 crate 中定义的还是在 `rand` crate 中定义的,这可以防止潜在的冲突。例如,`rand` crate 提供了一个名为 `Rng` 的特性trait。我们还可以在我们自己的 crate 中定义一个名为`Rng`的`struct`。因为一个 crate 的功能是在自身的作用域进行命名的,当我们将`rand`作为一个依赖,编译器不会混淆`Rng`这个名字的指向。在我们的 crate 中,它指向的是我们自己定义的`struct Rng`。我们可以通过`rand::Rng`这一方式来访问`rand` crate 中的 `Rng` 特性trait
将一个 crate 的功能保持在其自身的作用域中,可以知晓一些特定的功能是在我们的 crate 中定义的还是在 `rand` crate 中定义的,这可以防止潜在的冲突。例如,`rand` crate 提供了一个名为 `Rng` 的特性trait。我们还可以在我们自己的 crate 中定义一个名为 `Rng` `struct`。因为一个 crate 的功能是在自身的作用域进行命名的,当我们将 `rand` 作为一个依赖,编译器不会混淆 `Rng` 这个名字的指向。在我们的 crate 中,它指向的是我们自己定义的 `struct Rng`。我们可以通过 `rand::Rng` 这一方式来访问 `rand` crate 中的 `Rng` 特性trait
接下来让我们来说一说模块系统!

@ -4,13 +4,13 @@
> <br>
> commit 879fef2345bf32751a83a9e779e0cb84e79b6d3d
在本节,我们将讨论模块和其它一些关于模块系统的部分,如允许你命名项的*路径**paths*);用来将路径引入作用域的`use`关键字;以及使项变为公有的`pub`关键字。我们还将讨论`as`关键字、外部包和 glob 运算符。现在,让我们把注意力放在模块上!
在本节,我们将讨论模块和其它一些关于模块系统的部分,如允许你命名项的 *路径**paths*);用来将路径引入作用域的 `use` 关键字;以及使项变为公有的 `pub` 关键字。我们还将讨论 `as` 关键字、外部包和 glob 运算符。现在,让我们把注意力放在模块上!
*模块*让我们可以将一个 crate 中的代码进行分组,以提高可读性与重用性。模块还可以控制项的*私有性*,即项是可以被外部代码使用的(*public*),还是作为一个内部实现的内容,不能被外部代码使用(*private*)。
*模块* 让我们可以将一个 crate 中的代码进行分组,以提高可读性与重用性。模块还可以控制项的 *私有性*,即项是可以被外部代码使用的(*public*),还是作为一个内部实现的内容,不能被外部代码使用(*private*)。
在餐饮业,餐馆中会有一些地方被称之为*前台**front of house*),还有另外一些地方被称之为*后台**back of house*)。前台是招待顾客的地方,在这里,店主可以为顾客安排座位,服务员接受顾客下单和付款,调酒师会制作饮品。后台则是由厨师工作的厨房,洗碗工的工作地点,以及经理做行政工作的地方组成。
在餐饮业,餐馆中会有一些地方被称之为 *前台**front of house*),还有另外一些地方被称之为 *后台**back of house*)。前台是招待顾客的地方,在这里,店主可以为顾客安排座位,服务员接受顾客下单和付款,调酒师会制作饮品。后台则是由厨师工作的厨房,洗碗工的工作地点,以及经理做行政工作的地方组成。
我们可以将函数放置到嵌套的模块中,来使我们的 crate 结构与实际的餐厅结构相同。通过执行`cargo new --lib restaurant`,来创建一个新的名为`restaurant`的库。然后将示例 7-1中所罗列出来的代码放入*src/lib.rs*中,来定义一些模块和函数。
我们可以将函数放置到嵌套的模块中,来使我们的 crate 结构与实际的餐厅结构相同。通过执行 `cargo new --lib restaurant`,来创建一个新的名为 `restaurant` 的库。然后将示例 7-1 中所罗列出来的代码放入 *src/lib.rs* 中,来定义一些模块和函数。
Filename: src/lib.rs
@ -32,13 +32,13 @@ mod front_of_house {
}
```
<span class="caption">示例 7-1一个包含了其他内置了函数的模块的`front_of_house`模块</span>
<span class="caption">示例 7-1一个包含了其他内置了函数的模块的 `front_of_house` 模块</span>
我们定义一个模块,是以`mod`关键字为起始,然后指定模块的名字(本例中叫做`front_of_house`),并且用花括号包围模块的主体。在模块内,我们还可以定义其他的模块,就像本例中的`hosting`和`serving`模块。模块还可以保存一些定义的其他项,比如结构体、枚举、常量、特性、或者函数。
我们定义一个模块,是以 `mod` 关键字为起始,然后指定模块的名字(本例中叫做 `front_of_house`),并且用花括号包围模块的主体。在模块内,我们还可以定义其他的模块,就像本例中的 `hosting` `serving` 模块。模块还可以保存一些定义的其他项,比如结构体、枚举、常量、特性、或者函数。
通过使用模块,我们可以将相关的定义分组到一起,并指出他们为什么相关。程序员可以通过使用这段代码,更加容易地找到他们想要的定义,因为他们可以基于分组来对代码进行导航,而不需要阅读所有的定义。程序员向这段代码中添加一个新的功能时,他们也会知道代码应该放置在何处,可以保持程序的组织性。
在前面我们提到了,`src/main.rs`和`src/lib.rs`叫做 crate 根。之所以这样叫它们的原因是,这两个文件的内容都是一个从名为`crate`的模块作为根的 crate 模块结构,称为*模块树**module tree*)。
在前面我们提到了,`src/main.rs` `src/lib.rs` 叫做 crate 根。之所以这样叫它们的原因是,这两个文件的内容都是一个从名为 `crate` 的模块作为根的 crate 模块结构,称为 *模块树**module tree*)。
示例 7-2 展示了示例 7-1 中的模块树的结构。
@ -56,6 +56,6 @@ crate
<span class="caption">示例 7-2: 示例 7-1 中代码的模块树</span>
这个树展示了一些模块是如何被嵌入到另一个模块的(例如,`hosting`嵌套在`front_of_house`中)。这个树还展示了一些模块是互为*兄弟**siblings*)的,这意味着它们定义在同一模块中(`hosting`和`serving`被一起定义在`front_of_house`中)。继续沿用家庭关系的比喻,如果一个模块 A 被包含在模块 B 中,我们将模块 A 称为模块 B 的*子**child*),模块 B 则是模块 A 的*父**parent*)。注意,整个模块树都植根于名为`crate`的隐式模块下。
这个树展示了一些模块是如何被嵌入到另一个模块的(例如,`hosting` 嵌套在 `front_of_house` 中)。这个树还展示了一些模块是互为 *兄弟**siblings* 的,这意味着它们定义在同一模块中(`hosting` `serving` 被一起定义在 `front_of_house` 中)。继续沿用家庭关系的比喻,如果一个模块 A 被包含在模块 B 中,我们将模块 A 称为模块 B 的 *子**child*),模块 B 则是模块 A 的 *父**parent*)。注意,整个模块树都植根于名为 `crate` 的隐式模块下。
这个模块树可能会令你想起电脑上文件系统的目录树;这是一个非常恰当的比喻!就像文件系统的目录,你可以使用模块来组织你的代码。并且,就像目录中的文件,我们需要一种方法来找到模块。

@ -4,7 +4,7 @@
> <br>
> commit cc6a1ef2614aa94003566027b285b249ccf961fa
来看一下Rust如何在模块树中找到一个项的位置我们使用路径的方式就像在文件系统使用路径一样。如果我们想要调用一个函数我们需要知道它的路径。
来看一下 Rust 如何在模块树中找到一个项的位置,我们使用路径的方式,就像在文件系统使用路径一样。如果我们想要调用一个函数,我们需要知道它的路径。
路径有两种形式:
@ -13,7 +13,7 @@
绝对路径和相对路径都后跟一个或多个由双冒号(`::`)分割的标识符。
让我们回到示例 7-1。我们如何调用`add_to_waitlist`函数?还是同样的问题,`add_to_waitlist`函数的路径是什么?在示例 7-3 中,我们通过删除一些模块和函数,稍微简化了一下我们的代码。我们在 crate 根定义了一个新函数`eat_at_restaurant`,并在其中展示调用`add_to_waitlist`函数的两种方法。`eat_at_restaurant`函数是我们 crate 库的一个公共API所以我们使用`pub`关键字来标记它。在“[使用`pub`关键字暴露路径](https://github.com/rust-lang/book/blob/master/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#exposing-paths-with-the-pub-keyword)”一节,我们将详细介绍`pub`。注意,这个例子无法编译通过,我们稍后会解释原因。
让我们回到示例 7-1。我们如何调用 `add_to_waitlist` 函数?还是同样的问题,`add_to_waitlist` 函数的路径是什么?在示例 7-3 中,我们通过删除一些模块和函数,稍微简化了一下我们的代码。我们在 crate 根定义了一个新函数 `eat_at_restaurant`,并在其中展示调用 `add_to_waitlist` 函数的两种方法。`eat_at_restaurant` 函数是我们 crate 库的一个公共API所以我们使用 `pub` 关键字来标记它。在 “[使用`pub`关键字暴露路径](https://github.com/rust-lang/book/blob/master/src/ch07-03-paths-for-referring-to-an-item-in-the-module-tree.html#exposing-paths-with-the-pub-keyword)” 一节,我们将详细介绍 `pub`。注意,这个例子无法编译通过,我们稍后会解释原因。
<span class="filename">文件名: src/lib.rs</span>
@ -33,15 +33,15 @@ pub fn eat_at_restaurant() {
}
```
<span class="caption">示例 7-3: 使用绝对路径和相对路径来调用`add_to_waitlist`函数</span>
<span class="caption">示例 7-3: 使用绝对路径和相对路径来调用 `add_to_waitlist` 函数</span>
第一种方式,我们在`eat_at_restaurant`中调用`add_to_waitlist`函数,使用的是绝对路径。`add_to_waitlist`函数与`eat_at_restaurant`被定义在同一 crate 中,这意味着我们可以使用`crate`关键字为起始的绝对路径。
第一种方式,我们在 `eat_at_restaurant` 中调用 `add_to_waitlist` 函数,使用的是绝对路径。`add_to_waitlist` 函数与 `eat_at_restaurant` 被定义在同一 crate 中,这意味着我们可以使用 `crate` 关键字为起始的绝对路径。
在`crate`后面,我们持续地嵌入模块,直到我们找到`add_to_waitlist`。你可以想象出一个相同结构的文件系统,我们通过指定路径`/front_of_house/hosting/add_to_waitlist`来执行`add_to_waitlist`程序。我们使用 `crate` 从 crate 根开始就类似于在 shell 中使用 `/` 从文件系统根开始。
`crate` 后面,我们持续地嵌入模块,直到我们找到 `add_to_waitlist`。你可以想象出一个相同结构的文件系统,我们通过指定路径 `/front_of_house/hosting/add_to_waitlist` 来执行 `add_to_waitlist` 程序。我们使用 `crate` 从 crate 根开始就类似于在 shell 中使用 `/` 从文件系统根开始。
第二种方式,我们在`eat_at_restaurant`中调用`add_to_waitlist`,使用的是相对路径。这个路径以`front_of_house`为起始,这个模块在模块树中,与`eat_at_restaurant`定义在同一层级。与之等价的文件系统路径就是`front_of_house/hosting/add_to_waitlist`。以名称为起始,意味着该路径是相对路径。
第二种方式,我们在 `eat_at_restaurant` 中调用 `add_to_waitlist`,使用的是相对路径。这个路径以 `front_of_house` 为起始,这个模块在模块树中,与 `eat_at_restaurant` 定义在同一层级。与之等价的文件系统路径就是 `front_of_house/hosting/add_to_waitlist`。以名称为起始,意味着该路径是相对路径。
选择使用相对路径还是绝对路径,还是要取决于你的项目。取决于你是更倾向于将项的定义代码与使用该项的代码分来移动,还是一起移动。举一个例子,如果我们要将`front_of_house`模块和`eat_at_restaurant`函数一起移动到一个名为`customer_experience`的模块中,我们需要更新`add_to_waitlist`的绝对路径,但是相对路径还是可用的。然而,如果我们要将`eat_at_restaurant`函数单独移到一个名为`dining`的模块中,还是可以使用原本的绝对路径来调用`add_to_waitlist`,但是相对路径必须要更新。我们更倾向于使用绝对路径,因为它更适合移动代码定义和项调用的相互独立。
选择使用相对路径还是绝对路径,还是要取决于你的项目。取决于你是更倾向于将项的定义代码与使用该项的代码分来移动,还是一起移动。举一个例子,如果我们要将 `front_of_house` 模块和 `eat_at_restaurant` 函数一起移动到一个名为 `customer_experience` 的模块中,我们需要更新 `add_to_waitlist` 的绝对路径,但是相对路径还是可用的。然而,如果我们要将 `eat_at_restaurant` 函数单独移到一个名为 `dining` 的模块中,还是可以使用原本的绝对路径来调用 `add_to_waitlist`,但是相对路径必须要更新。我们更倾向于使用绝对路径,因为它更适合移动代码定义和项调用的相互独立。
让我们试着编译一下示例 7-3并查明为何不能编译示例 7-4 展示了这个错误。
@ -63,17 +63,17 @@ error[E0603]: module `hosting` is private
<span class="caption">示例 7-4: 构建示例 7-3 出现的编译器错误</span>
错误信息说`hosting`模块是私有的。换句话说,我们拥有`hosting`模块和`add_to_waitlist`函数的的正确路径,但是 Rust 不让我们使用,因为它不能访问私有片段。
错误信息说 `hosting` 模块是私有的。换句话说,我们拥有 `hosting` 模块和 `add_to_waitlist` 函数的的正确路径,但是 Rust 不让我们使用,因为它不能访问私有片段。
模块不仅对于你组织代码很有用。他们还定义了 Rust 的*私有性边界**privacy boundary*):这条界线不允许外部代码了解、调用和依赖被封装的实现细节。所以,如果你希望创建一个私有函数或结构体,你可以将其放入模块。
模块不仅对于你组织代码很有用。他们还定义了 Rust 的 *私有性边界**privacy boundary*):这条界线不允许外部代码了解、调用和依赖被封装的实现细节。所以,如果你希望创建一个私有函数或结构体,你可以将其放入模块。
Rust中默认所有项函数、方法、结构体、枚举、模块和常量都是私有的。父模块中的项不能使用子模块中的私有项但是子模块中的项可以使用他们父模块中的项。这是因为子模块封装并隐藏了他们的实现详情但是子模块可以看到他们定义的上下文。继续拿餐馆作比喻把私有性规则想象成餐馆的后台办公室餐馆内的事务对餐厅顾客来说是不可知的但办公室经理可以洞悉其经营的餐厅并在其中做任何事情。
Rust 中默认所有项(函数、方法、结构体、枚举、模块和常量)都是私有的。父模块中的项不能使用子模块中的私有项,但是子模块中的项可以使用他们父模块中的项。这是因为子模块封装并隐藏了他们的实现详情,但是子模块可以看到他们定义的上下文。继续拿餐馆作比喻,把私有性规则想象成餐馆的后台办公室:餐馆内的事务对餐厅顾客来说是不可知的,但办公室经理可以洞悉其经营的餐厅并在其中做任何事情。
Rust 选择以这种方式来实现模块系统功能,因此默认隐藏内部实现细节。这样一来,你就知道可以更改内部代码的哪些部分而不会破坏外部代码。你还可以通过使用`pub`关键字来创建公共项,使子模块的内部部分暴露给上级模块。
Rust 选择以这种方式来实现模块系统功能,因此默认隐藏内部实现细节。这样一来,你就知道可以更改内部代码的哪些部分而不会破坏外部代码。你还可以通过使用 `pub` 关键字来创建公共项,使子模块的内部部分暴露给上级模块。
### 使用`pub`关键字暴露路径
### 使用 `pub` 关键字暴露路径
让我们回头看一下示例 7-4 的错误,它告诉我们`hosting`模块是私有的。我们想让父模块中的`eat_at_restaurant`函数可以访问子模块中的`add_to_waitlist`函数,因此我们使用`pub`关键字来标记`hosting`模块,如示例 7-5 所示。
让我们回头看一下示例 7-4 的错误,它告诉我们 `hosting` 模块是私有的。我们想让父模块中的 `eat_at_restaurant` 函数可以访问子模块中的 `add_to_waitlist` 函数,因此我们使用 `pub` 关键字来标记 `hosting` 模块,如示例 7-5 所示。
<span class="filename">文件名: src/lib.rs</span>
@ -115,11 +115,11 @@ error[E0603]: function `add_to_waitlist` is private
<span class="caption">示例 7-6: 构建示例 7-5 出现的编译器错误</span>
发生了什么?在`mod hosting`前添加了`pub`关键字,使其变成公有的。伴随着这种变化,如果我们可以访问`front_of_house`,那我们也可以访问`hosting`。但是`hosting`的*内容**contents*)仍然是私有的;这表明使模块公有并不使其内容也是公有的。模块上的 `pub` 关键字只允许其父模块引用它。
发生了什么?在 `mod hosting` 前添加了 `pub` 关键字,使其变成公有的。伴随着这种变化,如果我们可以访问 `front_of_house`,那我们也可以访问 `hosting`。但是 `hosting` *内容**contents* 仍然是私有的;这表明使模块公有并不使其内容也是公有的。模块上的 `pub` 关键字只允许其父模块引用它。
示例 7-6 中的错误说,`add_to_waitlist`函数是私有的。私有性规则不但应用于模块,还应用于结构体、枚举、函数和方法。
示例 7-6 中的错误说,`add_to_waitlist` 函数是私有的。私有性规则不但应用于模块,还应用于结构体、枚举、函数和方法。
让我们继续将`pub`关键字放置在`add_to_waitlist`函数的定义之前,使其变成公有。如示例 7-7 所示。
让我们继续将 `pub` 关键字放置在 `add_to_waitlist` 函数的定义之前,使其变成公有。如示例 7-7 所示。
<span class="filename">文件名: src/lib.rs</span>
@ -142,11 +142,11 @@ pub fn eat_at_restaurant() {
<span class="caption">示例 7-7: 为 `mod hosting`
`fn add_to_waitlist` 添加 `pub` 关键字使他们可以在
`eat_at_restaurant`函数中被调用</span>
`eat_at_restaurant` 函数中被调用</span>
现在代码可以编译通过了!让我们看看绝对路径和相对路径,并根据私有性规则,再检查一下为什么增加 `pub` 关键字使得我们可以在 `add_to_waitlist` 中调用这些路径。
在绝对路径,我们从 `crate`,也就是 crate 根开始。然后 crate 根中定义了 `front_of_house` 模块。`front_of_house` 模块不是公有的,不过因为 `eat_at_restaurant` 函数与 `front_of_house` 定义于同一模块中(即,`eat_at_restaurant`和`front_of_house`是兄弟),我们可以从 `eat_at_restaurant` 中引用 `front_of_house`。接下来是使用 `pub` 标记的 `hosting` 模块。我们可以访问 `hosting` 的父模块,所以可以访问 `hosting`。最后,`add_to_waitlist` 函数被标记为 `pub` ,我们可以访问其父模块,所以这个函数调用是有效的!
在绝对路径,我们从 `crate`,也就是 crate 根开始。然后 crate 根中定义了 `front_of_house` 模块。`front_of_house` 模块不是公有的,不过因为 `eat_at_restaurant` 函数与 `front_of_house` 定义于同一模块中(即,`eat_at_restaurant` `front_of_house` 是兄弟),我们可以从 `eat_at_restaurant` 中引用 `front_of_house`。接下来是使用 `pub` 标记的 `hosting` 模块。我们可以访问 `hosting` 的父模块,所以可以访问 `hosting`。最后,`add_to_waitlist` 函数被标记为 `pub` ,我们可以访问其父模块,所以这个函数调用是有效的!
在相对路径,其逻辑与绝对路径相同,除了第一步:不同于从 crate 根开始,路径从 `front_of_house` 开始。`front_of_house` 模块与 `eat_at_restaurant` 定义于同一模块,所以从 `eat_at_restaurant` 中开始定义的该模块相对路径是有效的。接下来因为 `hosting``add_to_waitlist` 被标记为 `pub`,路径其余的部分也是有效的,因此函数调用也是有效的!
@ -154,7 +154,7 @@ pub fn eat_at_restaurant() {
我们还可以使用 `super` 开头来构建从父模块开始的相对路径。这么做类似于文件系统中以 `..` 开头的语法。我们为什么要这样做呢?
考虑一下示例 7-8 中的代码,它模拟了厨师更正了一个错误订单,并亲自将其提供给客户的情况。`fix_incorrect_order`函数通过指定的 `super` 起始的 `server_order` 路径,来调用 `server_order`函数:
考虑一下示例 7-8 中的代码,它模拟了厨师更正了一个错误订单,并亲自将其提供给客户的情况。`fix_incorrect_order` 函数通过指定的 `super` 起始的 `server_order` 路径,来调用 `server_order` 函数:
<span class="filename">文件名: src/lib.rs</span>
@ -174,7 +174,7 @@ mod back_of_house {
<span class="caption">示例 7-8: 使用以 `super` 开头的相对路径从父目录开始调用函数</span>
`fix_incorrect_order` 函数在 `back_of_house` 模块中,所以我们可以使用 `super` 进入 `back_of_house` 父模块,也就是本例中的 `crate` 根。在这里,我们可以找到`serve_order`。成功!我们认为`back_of_house`模块和`server_order`函数之间可能具有某种关联关系,并且,如果我们要重新组织这个 crate 的模块树,需要一起移动它们。因此,我们使用 `super` ,这样一来,如果这些代码被移动到了其他模块,我们只需要更新很少的代码。
`fix_incorrect_order` 函数在 `back_of_house` 模块中,所以我们可以使用 `super` 进入 `back_of_house` 父模块,也就是本例中的 `crate` 根。在这里,我们可以找到 `serve_order`。成功!我们认为 `back_of_house` 模块和 `server_order` 函数之间可能具有某种关联关系,并且,如果我们要重新组织这个 crate 的模块树,需要一起移动它们。因此,我们使用 `super`,这样一来,如果这些代码被移动到了其他模块,我们只需要更新很少的代码。
### 创建公有的结构体和枚举
@ -214,9 +214,9 @@ pub fn eat_at_restaurant() {
<span class="caption">示例 7-9: 带有公有和私有字段的结构体</span>
因为 `back_of_house::Breakfast`结构体的 `toast` 字段是公有的,所以我们可以在 `eat_at_restaurant` 中使用点号来随意的读写 `toast` 字段。注意,我们不能在 `eat_at_restaurant` 中使用 `seasonal_fruit` 字段,因为 `seasonal_fruit` 是私有的。尝试去除那一行修改 `seasonal_fruit` 字段值的代码的注释,看看你会得到什么错误!
因为 `back_of_house::Breakfast` 结构体的 `toast` 字段是公有的,所以我们可以在 `eat_at_restaurant` 中使用点号来随意的读写 `toast` 字段。注意,我们不能在 `eat_at_restaurant` 中使用 `seasonal_fruit` 字段,因为 `seasonal_fruit` 是私有的。尝试去除那一行修改 `seasonal_fruit` 字段值的代码的注释,看看你会得到什么错误!
还请注意一点,因为 `back_of_house::Breakfast` 具有私有字段,所以这个结构体需要提供一个公共的关联函数来构造示例 `Breakfast` (这里我们命名为 `summer`)。如果 `Breakfast` 没有这样的函数,我们将无法在 `eat_at_restaurant`中创建 `Breakfast` 实例,因为我们不能在 `eat_at_restaurant` 中设置私有字段 `seasonal_fruit` 的值。
还请注意一点,因为 `back_of_house::Breakfast` 具有私有字段,所以这个结构体需要提供一个公共的关联函数来构造示例 `Breakfast` (这里我们命名为 `summer`)。如果 `Breakfast` 没有这样的函数,我们将无法在 `eat_at_restaurant` 中创建 `Breakfast` 实例,因为我们不能在 `eat_at_restaurant` 中设置私有字段 `seasonal_fruit` 的值。
与之相反,如果我们将枚举设为公有,则它的所有成员都将变为公有。我们只需要在 `enum` 关键字前面加上 `pub`,就像示例 7-10 展示的那样。

@ -4,7 +4,7 @@
> <br>
> commit 6d3e76820418f2d2bb203233c61d90390b5690f1
到目前为止,似乎我们编写的用于调用函数的路径都很冗长且重复,并不方便。例如,示例 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` 关键字调用该路径中的项,就如同它们是本地项一样。
在示例 7-11 中,我们将 `crate::front_of_house::hosting` 模块引入了 `eat_at_restaurant` 函数的作用域,而我们只需要指定 `hosting::add_to_waitlist` 即可在 `eat_at_restaurant` 中调用 `add_to_waitlist` 函数。
@ -148,7 +148,7 @@ fn function2() -> IoResult<()> {
### 使用 `pub use` 重导出名称
当使用 `use` 关键字将名称导入作用域时,在新作用域中可用的名称是私有的。如果为了让调用你编写的代码的代码能够像在自己的作用域内引用这些类型,可以结合 `pub``use`。这个技术被称为 “重导出”(*re-exporting*,因为这样做将项引入作用域并同时使其可供其他代码引入自己的作用域。
当使用 `use` 关键字将名称导入作用域时,在新作用域中可用的名称是私有的。如果为了让调用你编写的代码的代码能够像在自己的作用域内引用这些类型,可以结合 `pub``use`。这个技术被称为 “*重导出**re-exporting*)”,因为这样做将项引入作用域并同时使其可供其他代码引入自己的作用域。
示例 7-17 展示了将示例 7-11 中使用 `use` 的根模块变为 `pub use` 的版本的代码。

Loading…
Cancel
Save