Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

使用模块控制作用域和隐私

Control Scope and Privacy with Modules

在本节中,我们将讨论模块和模块系统的其他部分,即:允许你命名的路径paths);将路径引入作用域的 use 关键字;以及使项变为公开的 pub 关键字。我们还将讨论 as 关键字、外部 package 以及 glob 运算符。

In this section, we’ll talk about modules and other parts of the module system, namely paths, which allow you to name items; the use keyword that brings a path into scope; and the pub keyword to make items public. We’ll also discuss the as keyword, external packages, and the glob operator.

模块速查表

Modules Cheat Sheet

在深入了解模块和路径的细节之前,这里我们提供了一个关于模块、路径、use 关键字和 pub 关键字在编译器中如何工作,以及大多数开发人员如何组织他们的代码的快速参考。我们将在本章中贯穿这些规则的示例,但这是一个提醒模块如何工作的绝佳参考。

Before we get to the details of modules and paths, here we provide a quick reference on how modules, paths, the use keyword, and the pub keyword work in the compiler, and how most developers organize their code. We’ll be going through examples of each of these rules throughout this chapter, but this is a great place to refer to as a reminder of how modules work.

  • 从 crate root 开始:在编译 crate 时,编译器首先在 crate root 文件(通常库 crate 为 src/lib.rs,二进制 crate 为 src/main.rs)中寻找要编译的代码。

  • Start from the crate root: When compiling a crate, the compiler first looks in the crate root file (usually src/lib.rs for a library crate and src/main.rs for a binary crate) for code to compile.

  • 声明模块:在 crate root 文件中,你可以声明新模块;假设你用 mod garden; 声明了一个 “garden” 模块。编译器将在以下位置寻找模块的代码:

  • Declaring modules: In the crate root file, you can declare new modules; say you declare a “garden” module with mod garden;. The compiler will look for the module’s code in these places:

    • 内联(Inline),在取代 mod garden 后面分号的花括号内。

    • Inline, within curly brackets that replace the semicolon following mod garden

    • 在文件 src/garden.rs 中。

    • In the file src/garden.rs

    • 在文件 src/garden/mod.rs 中。

    • In the file src/garden/mod.rs

  • 声明子模块:在除 crate root 以外的任何文件中,你都可以声明子模块。例如,你可能在 src/garden.rs 中声明 mod vegetables;。编译器将在以父模块命名的目录下寻找子模块的代码:

  • Declaring submodules: In any file other than the crate root, you can declare submodules. For example, you might declare mod vegetables; in src/garden.rs. The compiler will look for the submodule’s code within the directory named for the parent module in these places:

    • 内联,直接跟在 mod vegetables 后面,放在花括号内而不是分号。

    • Inline, directly following mod vegetables, within curly brackets instead of the semicolon

    • 在文件 src/garden/vegetables.rs 中。

    • In the file src/garden/vegetables.rs

    • 在文件 src/garden/vegetables/mod.rs 中。

    • In the file src/garden/vegetables/mod.rs

  • 模块中代码的路径:一旦模块成为你 crate 的一部分,你就可以从同一个 crate 的任何其他地方引用该模块中的代码,只要隐私规则允许,使用代码的路径即可。例如,garden vegetables 模块中的 Asparagus 类型将在 crate::garden::vegetables::Asparagus 找到。

  • Paths to code in modules: Once a module is part of your crate, you can refer to code in that module from anywhere else in that same crate, as long as the privacy rules allow, using the path to the code. For example, an Asparagus type in the garden vegetables module would be found at crate::garden::vegetables::Asparagus.

  • 私有与公开:默认情况下,模块内的代码对其父模块是私有的。要使模块公开,请使用 pub mod 而不是 mod 来声明它。要使公开模块内的项也公开,请在它们的声明前使用 pub

  • Private vs. public: Code within a module is private from its parent modules by default. To make a module public, declare it with pub mod instead of mod. To make items within a public module public as well, use pub before their declarations.

  • use 关键字:在一个作用域内,use 关键字可以为项创建快捷方式,以减少长路径的重复。在任何可以引用 crate::garden::vegetables::Asparagus 的作用域内,你都可以使用 use crate::garden::vegetables::Asparagus; 创建快捷方式,从那时起你只需要编写 Asparagus 即可在作用域内使用该类型。

  • The use keyword: Within a scope, the use keyword creates shortcuts to items to reduce repetition of long paths. In any scope that can refer to crate::garden::vegetables::Asparagus, you can create a shortcut with use crate::garden::vegetables::Asparagus;, and from then on you only need to write Asparagus to make use of that type in the scope.

在这里,我们创建一个名为 backyard 的二进制 crate 来说明这些规则。该 crate 的目录(也名为 backyard)包含这些文件和目录:

Here, we create a binary crate named backyard that illustrates these rules. The crate’s directory, also named backyard, contains these files and directories:

backyard
├── Cargo.lock
├── Cargo.toml
└── src
    ├── garden
    │   └── vegetables.rs
    ├── garden.rs
    └── main.rs

在这种情况下,crate root 文件是 src/main.rs,它包含:

The crate root file in this case is src/main.rs, and it contains:

use crate::garden::vegetables::Asparagus;

pub mod garden;

fn main() {
    let plant = Asparagus {};
    println!("I'm growing {plant:?}!");
}

pub mod garden; 这行告诉编译器包含它在 src/garden.rs 中找到的代码,即:

The pub mod garden; line tells the compiler to include the code it finds in src/garden.rs, which is:

pub mod vegetables;

在这里,pub mod vegetables; 意味着 src/garden/vegetables.rs 中的代码也被包含了。该代码是:

Here, pub mod vegetables; means the code in src/garden/vegetables.rs is included too. That code is:

#[derive(Debug)]
pub struct Asparagus {}

现在让我们深入了解这些规则的细节,并演示它们的实际应用!

Now let’s get into the details of these rules and demonstrate them in action!

在模块中对相关代码进行分组

模块让我们能够为了可读性和易重用性而在 crate 内组织代码。模块还允许我们控制项的隐私privacy),因为模块内的代码默认是私有的。私有项是不可供外部使用的内部实现细节。我们可以选择将模块及其内部的项设为公开,这会暴露它们,以允许外部代码使用并依赖它们。

Modules let us organize code within a crate for readability and easy reuse. Modules also allow us to control the privacy of items because code within a module is private by default. Private items are internal implementation details not available for outside use. We can choose to make modules and the items within them public, which exposes them to allow external code to use and depend on them.

作为一个例子,让我们编写一个提供餐厅功能的库 crate。我们将定义函数的签名,但保持其主体为空,以专注于代码的组织,而不是餐厅的实现。

As an example, let’s write a library crate that provides the functionality of a restaurant. We’ll define the signatures of functions but leave their bodies empty to concentrate on the organization of the code rather than the implementation of a restaurant.

在餐饮业中,餐厅的某些部分被称为“前台”(front of house),其他部分被称为“后台”(back of house)。前台是顾客所在的地方;这包括接待员为顾客安排座位的地方、服务员接单和结账的地方,以及调酒师调酒的地方。后台是主厨和厨师在厨房工作、洗碗工清理以及经理进行管理工作的地方。

In the restaurant industry, some parts of a restaurant are referred to as front of house and others as back of house. Front of house is where customers are; this encompasses where the hosts seat customers, servers take orders and payment, and bartenders make drinks. Back of house is where the chefs and cooks work in the kitchen, dishwashers clean up, and managers do administrative work.

为了以这种方式构建我们的 crate,我们可以将其函数组织到嵌套模块中。通过运行 cargo new restaurant --lib 创建一个名为 restaurant 的新库。然后,将示例 7-1 中的代码输入 src/lib.rs 以定义一些模块和函数签名;此代码是前台部分。

To structure our crate in this way, we can organize its functions into nested modules. Create a new library named restaurant by running cargo new restaurant --lib. Then, enter the code in Listing 7-1 into src/lib.rs to define some modules and function signatures; this code is the front of house section.

mod front_of_house {
    mod hosting {
        fn add_to_waitlist() {}

        fn seat_at_table() {}
    }

    mod serving {
        fn take_order() {}

        fn serve_order() {}

        fn take_payment() {}
    }
}

我们使用 mod 关键字后跟模块名称(在本例中为 front_of_house)来定义模块。然后,模块的主体放在花括号内。在模块内,我们可以放置其他模块,如本例中的 hostingserving 模块。模块还可以持有其他项的定义,例如结构体、枚举、常量、trait,以及如示例 7-1 所示的函数。

We define a module with the mod keyword followed by the name of the module (in this case, front_of_house). The body of the module then goes inside curly brackets. Inside modules, we can place other modules, as in this case with the modules hosting and serving. Modules can also hold definitions for other items, such as structs, enums, constants, traits, and as in Listing 7-1, functions.

通过使用模块,我们可以将相关的定义组合在一起,并命名它们相关的原因。使用此代码的程序员可以根据这些分组来浏览代码,而不必通读所有定义,从而更容易找到与他们相关的定义。向此代码添加新功能的程序员将知道将代码放在哪里以保持程序的组织性。

By using modules, we can group related definitions together and name why they’re related. Programmers using this code can navigate the code based on the groups rather than having to read through all the definitions, making it easier to find the definitions relevant to them. Programmers adding new functionality to this code would know where to place the code to keep the program organized.

早些时候,我们提到 src/main.rssrc/lib.rs 被称为 crate root。它们名称的原因是这两个文件中的任何一个的内容都在 crate 模块结构的根部形成了一个名为 crate 的模块,这就是所谓的“模块树”(module tree)。

Earlier, we mentioned that src/main.rs and src/lib.rs are called crate roots. The reason for their name is that the contents of either of these two files form a module named crate at the root of the crate’s module structure, known as the module tree.

示例 7-2 展示了示例 7-1 中结构的模块树。

Listing 7-2 shows the module tree for the structure in Listing 7-1.

crate
 └── front_of_house
     ├── hosting
     │   ├── add_to_waitlist
     │   └── seat_at_table
     └── serving
         ├── take_order
         ├── serve_order
         └── take_payment

此树展示了某些模块如何嵌套在其他模块中;例如,hosting 嵌套在 front_of_house 内部。该树还展示了某些模块是“兄弟”(siblings),这意味着它们定义在同一个模块中;hostingserving 是定义在 front_of_house 内的兄弟。如果模块 A 包含在模块 B 内,我们说模块 A 是模块 B 的“子”(child),而模块 B 是模块 A 的“父”(parent)。请注意,整个模块树都植根于名为 crate 的隐式模块之下。

This tree shows how some of the modules nest inside other modules; for example, hosting nests inside front_of_house. The tree also shows that some modules are siblings, meaning they’re defined in the same module; hosting and serving are siblings defined within front_of_house. If module A is contained inside module B, we say that module A is the child of module B and that module B is the parent of module A. Notice that the entire module tree is rooted under the implicit module named crate.

模块树可能会让你想起电脑文件系统上的目录树;这是一个非常恰当的类比!就像文件系统中的目录一样,你使用模块来组织你的代码。就像目录中的文件一样,我们需要一种方法来寻找我们的模块。

The module tree might remind you of the filesystem’s directory tree on your computer; this is a very apt comparison! Just like directories in a filesystem, you use modules to organize your code. And just like files in a directory, we need a way to find our modules.