将模块分离到不同的文件
到目前为止,本章中的所有示例都在一个文件中定义了多个模块。当模块变得很大时,您可能希望将其定义移动到单独的文件中,以使代码更易于导航。
例如,让我们从清单 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 文件之前,这将无法编译。
文件名: src/lib.rs
mod front_of_house;
pub use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
清单 7-21: 声明 front_of_house
模块,其主体将在 src/front_of_house.rs 中
接下来,将花括号内的代码放入名为 src/front_of_house.rs 的新文件中,如清单 7-22 所示。编译器知道在此文件中查找,因为它在 crate 根目录中遇到了名为 front_of_house
的模块声明。
文件名: src/front_of_house.rs
pub mod hosting {
pub fn add_to_waitlist() {}
}
清单 7-22: src/front_of_house.rs 中 front_of_house
模块内的定义
请注意,在模块树中,您只需要使用 mod
声明加载文件一次。一旦编译器知道该文件是项目的一部分(并且由于您放置 mod
语句的位置而知道代码位于模块树中的哪个位置),项目中的其他文件应使用指向其声明位置的路径来引用已加载文件的代码,如 “在模块树中引用项的路径”部分中所述。换句话说,mod
不是您可能在其他编程语言中看到的 “include” 操作。
接下来,我们将 hosting
模块提取到其自己的文件中。此过程略有不同,因为 hosting
是 front_of_house
的子模块,而不是根模块的子模块。我们将把 hosting
的文件放在一个新目录中,该目录将以其在模块树中的祖先命名,在本例中为 src/front_of_house。
要开始移动 hosting
,我们更改 src/front_of_house.rs 以仅包含 hosting
模块的声明
文件名: src/front_of_house.rs
pub mod hosting;
然后,我们创建一个 src/front_of_house 目录和一个 hosting.rs 文件,以包含在 hosting
模块中进行的定义
文件名: src/front_of_house/hosting.rs
pub fn add_to_waitlist() {}
如果我们改为将 hosting.rs 放在 src 目录中,编译器将期望 hosting.rs 代码位于 crate 根目录中声明的 hosting
模块中,而不是声明为 front_of_house
模块的子模块。编译器关于检查哪些文件以查找哪些模块代码的规则意味着目录和文件更紧密地匹配模块树。
备用文件路径
到目前为止,我们已经介绍了 Rust 编译器使用的最惯用的文件路径,但 Rust 也支持较旧的文件路径样式。对于在 crate 根目录中声明的名为 front_of_house
的模块,编译器将在以下位置查找模块的代码:
- src/front_of_house.rs (我们介绍的内容)
- src/front_of_house/mod.rs (较旧的样式,仍然支持的路径)
对于名为 hosting
的模块,它是 front_of_house
的子模块,编译器将在以下位置查找模块的代码:
- src/front_of_house/hosting.rs (我们介绍的内容)
- src/front_of_house/hosting/mod.rs (较旧的样式,仍然支持的路径)
如果对同一模块使用两种样式,则会收到编译器错误。对于同一项目中的不同模块混合使用两种样式是允许的,但可能会使浏览项目的人感到困惑。
使用名为 mod.rs 的文件的样式的主要缺点是,您的项目最终可能会有许多名为 mod.rs 的文件,当您同时在编辑器中打开它们时,这可能会让人感到困惑。
我们已将每个模块的代码移动到单独的文件中,模块树保持不变。eat_at_restaurant
中的函数调用将无需任何修改即可工作,即使定义位于不同的文件中。此技术使您可以随着模块大小的增长而将其移动到新文件中。
请注意,src/lib.rs 中的 pub use crate::front_of_house::hosting
语句也没有更改,use
也不会对编译为 crate 一部分的文件产生任何影响。mod
关键字声明模块,Rust 在与模块同名的文件中查找进入该模块的代码。
总结
Rust 允许您将一个包拆分为多个 crate,并将一个 crate 拆分为模块,以便您可以从另一个模块引用在一个模块中定义的项。您可以通过指定绝对路径或相对路径来执行此操作。这些路径可以使用 use
语句引入作用域,以便您可以为该作用域中项的多次使用使用较短的路径。模块代码默认是私有的,但是您可以通过添加 pub
关键字使定义公开。
在下一章中,我们将介绍标准库中的一些集合数据结构,您可以在组织良好的代码中使用它们。