将模块拆分为不同的文件
Separating Modules into Different Files
到目前为止,本章中的所有示例都在一个文件中定义了多个模块。当模块变得很大时,你可能想将其定义移动到单独的文件中,以使代码更易于浏览。
So far, all the examples in this chapter defined multiple modules in one file. When modules get large, you might want to move their definitions to a separate file to make the code easier to navigate.
例如,让我们从具有多个餐厅模块的示例 7-17 中的代码开始。我们将把模块提取到文件中,而不是在 crate root 文件中定义所有模块。在这种情况下,crate root 文件是 src/lib.rs,但此过程也适用于 crate root 文件为 src/main.rs 的二进制 crate。
For example, let’s start from the code in Listing 7-17 that had multiple restaurant modules. We’ll extract modules into files instead of having all the modules defined in the crate root file. In this case, the crate root file is src/lib.rs, but this procedure also works with binary crates whose crate root file is src/main.rs.
首先,我们将 front_of_house 模块提取到它自己的文件中。删除 front_of_house 模块花括号内的代码,仅留下 mod front_of_house; 声明,使得 src/lib.rs 包含示例 7-21 所示的代码。注意,在我们在示例 7-22 中创建 src/front_of_house.rs 文件之前,这段代码将无法编译。
First, we’ll extract the front_of_house module to its own file. Remove the
code inside the curly brackets for the front_of_house module, leaving only
the mod front_of_house; declaration, so that src/lib.rs contains the code
shown in Listing 7-21. Note that this won’t compile until we create the
src/front_of_house.rs file in Listing 7-22.
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
接下来,将花括号中的代码放入名为 src/front_of_house.rs 的新文件中,如示例 7-22 所示。编译器知道要在这个文件中查找,因为它在 crate root 中遇到了名为 front_of_house 的模块声明。
Next, place the code that was in the curly brackets into a new file named
src/front_of_house.rs, as shown in Listing 7-22. The compiler knows to look
in this file because it came across the module declaration in the crate root
with the name front_of_house.
pub mod hosting {
pub fn add_to_waitlist() {}
}
注意,在模块树中,你只需要使用 mod 声明来加载一个文件一次。一旦编译器知道该文件是项目的一部分(并且由于你放置 mod 语句的位置而知道该代码在模块树中的位置),项目中的其他文件应该使用指向其声明位置的路径来引用加载文件的代码,如“引用模块树中项的路径”部分所述。换句话说,mod 不是你在其他编程语言中可能见过的 “include” 操作。
Note that you only need to load a file using a mod declaration once in your
module tree. Once the compiler knows the file is part of the project (and knows
where in the module tree the code resides because of where you’ve put the mod
statement), other files in your project should refer to the loaded file’s code
using a path to where it was declared, as covered in the “Paths for Referring
to an Item in the Module Tree” section. In other words,
mod is not an “include” operation that you may have seen in other
programming languages.
接下来,我们将 hosting 模块提取到它自己的文件中。这个过程略有不同,因为 hosting 是 front_of_house 的子模块,而不是根模块的子模块。我们将 hosting 的文件放在一个新目录中,该目录将以其在模块树中的祖先命名,在本例中为 src/front_of_house。
Next, we’ll extract the hosting module to its own file. The process is a bit
different because hosting is a child module of front_of_house, not of the
root module. We’ll place the file for hosting in a new directory that will be
named for its ancestors in the module tree, in this case src/front_of_house.
要开始移动 hosting,我们更改 src/front_of_house.rs 以仅包含 hosting 模块的声明:
To start moving hosting, we change src/front_of_house.rs to contain only
the declaration of the hosting module:
pub mod hosting;
然后,我们创建一个 src/front_of_house 目录和一个 hosting.rs 文件,以包含在 hosting 模块中进行的定义:
Then, we create a src/front_of_house directory and a hosting.rs file to
contain the definitions made in the hosting module:
pub fn add_to_waitlist() {}
如果我们相反将 hosting.rs 放在 src 目录中,编译器会期望 hosting.rs 的代码位于 crate root 中声明的 hosting 模块中,而不是声明为 front_of_house 模块的子模块。编译器关于在哪些文件中查找哪些模块代码的规则意味着目录和文件与模块树更加匹配。
If we instead put hosting.rs in the src directory, the compiler would
expect the hosting.rs code to be in a hosting module declared in the crate
root and not declared as a child of the front_of_house module. The
compiler’s rules for which files to check for which modules’ code mean the
directories and files more closely match the module tree.
替代文件路径
Alternate File Paths
到目前为止,我们已经介绍了 Rust 编译器使用的最惯用的文件路径,但 Rust 也支持一种旧式的文件路径。对于在 crate root 中声明的名为
front_of_house的模块,编译器将在以下位置寻找模块代码:So far we’ve covered the most idiomatic file paths the Rust compiler uses, but Rust also supports an older style of file path. For a module named
front_of_housedeclared in the crate root, the compiler will look for the module’s code in:
- src/front_of_house.rs(我们介绍过的)
- src/front_of_house.rs (what we covered)
- src/front_of_house/mod.rs(旧式路径,仍然支持)
- src/front_of_house/mod.rs (older style, still supported path)
对于作为
front_of_house子模块的名为hosting的模块,编译器将在以下位置寻找模块代码:For a module named
hostingthat is a submodule offront_of_house, the compiler will look for the module’s code in:
- src/front_of_house/hosting.rs(我们介绍过的)
- src/front_of_house/hosting.rs (what we covered)
- src/front_of_house/hosting/mod.rs(旧式路径,仍然支持)
- src/front_of_house/hosting/mod.rs (older style, still supported path)
如果你对同一个模块同时使用两种风格,你将得到一个编译器错误。在同一个项目中的不同模块混合使用两种风格是允许的,但可能会让浏览你项目的人感到困惑。
If you use both styles for the same module, you’ll get a compiler error. Using a mix of both styles for different modules in the same project is allowed but might be confusing for people navigating your project.
使用名为 mod.rs 的文件的主要缺点是,你的项目最终可能会有许多名为 mod.rs 的文件,当你同时在编辑器中打开它们时,这可能会变得令人困惑。
The main downside to the style that uses files named mod.rs is that your project can end up with many files named mod.rs, which can get confusing when you have them open in your editor at the same time.
我们已经将每个模块的代码移动到单独的文件中,模块树保持不变。即使定义位于不同的文件中,eat_at_restaurant 中的函数调用无需任何修改即可工作。这种技术让你可以在模块大小增长时将其移动到新文件中。
We’ve moved each module’s code to a separate file, and the module tree remains
the same. The function calls in eat_at_restaurant will work without any
modification, even though the definitions live in different files. This
technique lets you move modules to new files as they grow in size.
注意 src/lib.rs 中的 pub use crate::front_of_house::hosting 语句也没有改变,use 对作为 crate 一部分编译哪些文件也没有任何影响。mod 关键字声明模块,Rust 会在与模块同名的文件中查找进入该模块的代码。
Note that the pub use crate::front_of_house::hosting statement in
src/lib.rs also hasn’t changed, nor does use have any impact on what files
are compiled as part of the crate. The mod keyword declares modules, and Rust
looks in a file with the same name as the module for the code that goes into
that module.
总结
Summary
Rust 允许你将 package 拆分为多个 crate,将 crate 拆分为多个模块,以便你可以从一个模块引用另一个模块中定义的项。你可以通过指定绝对或相对路径来实现这一点。这些路径可以使用 use 语句引入作用域,以便你在该作用域内多次使用该项时可以使用较短的路径。默认情况下,模块代码是私有的,但你可以通过添加 pub 关键字使定义变为公开。
Rust lets you split a package into multiple crates and a crate into modules so
that you can refer to items defined in one module from another module. You can
do this by specifying absolute or relative paths. These paths can be brought
into scope with a use statement so that you can use a shorter path for
multiple uses of the item in that scope. Module code is private by default, but
you can make definitions public by adding the pub keyword.
在下一章中,我们将看看标准库中的一些集合数据结构,你可以在你整洁组织的代码中使用它们。
In the next chapter, we’ll look at some collection data structures in the standard library that you can use in your neatly organized code.