7.5将模块分离到不同的文件中
将模块分离到不同的文件中
到目前为止,本章中的所有示例都在一个文件中定义了多个模块。当模块变得庞大时,你可能希望将它们的定义移到单独的文件中,以便更容易浏览代码。
例如,我们从清单7-17中的代码开始,该代码包含多个餐厅模块。我们将把模块提取到各自的文件,而不是让所有模块都定义在crate根文件中。在这种情况下,crate根文件是src/lib.rs,但这个过程同样适用于二进制crate,其crate根文件是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文件之前,这段代码无法编译。
文件名: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 并不是你在其他编程语言中见过的“包含”操作。
接下来,我们将把 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 目录下,编译器会期望该代码属于 crate 根部声明的 hosting 模块,而非作为 front_of_house 子模块声明。编译器检查各个模块对应哪些代码所在文件时,有一套规则,这使得目录和文件结构更贴合模块树结构。
备用路径方式
到目前为止,我们介绍了 Rust 编译器最惯用的路径,但 Rust 同样支持一种较旧风格的路径方式。对于在 crate 根部声明、名为 front_of_house 的模块,编译器会查找以下位置:
- src/front_of_house.rs(前面介绍过)
- src/front_of_house/mod.rs(较旧风格,仍被支持)
对于作为 front_of_house 子模块、名为 hosting 的模块,编译器会查找以下位置:
- 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 关键字修饰。
下一章,我们将继续探讨一些…