Rust 学习笔记 11:包与模块 (Packages & Modules)

“Organization is what you do before you do it, so that when you do it, it’s not all mixed up.” – A. A. Milne

欢迎来到 2025 年! 在完成了前 10 章的基础语法学习后,我们现在的 Rust 代码全都写在一个 main.rs 里。这对于写个猜数字游戏还行,但要写大项目,文件组织是必修课。

Rust 的模块系统 (Module System) 有点复杂,甚至被称为"新手劝退三大难"之一(另外俩是所有权和生命周期)。

1. 家族谱系:Package, Crate, Module

首先要分清三个概念:

  1. Package (包):Cargo 的功能单元。包含 Cargo.toml。一个 Package 可以包含多个 Binary Crate,但只能有一个 Library Crate。
  2. Crate (箱):编译单元。main.rs 是二进制 Crate 的根,lib.rs 是库 Crate 的根。
  3. Module (模块):代码组织单元。用 mod 关键字定义。

层级关系:Package -> (contains) -> Crates -> (contains) -> Modules。

2. 定义模块 (mod)

我们用一个经典的"餐馆"例子来理解。

lib.rs 中:

1pub mod front_of_house {
2    pub mod hosting {
3        pub fn add_to_waitlist() {}
4    }
5}

注意那个 pub。在 Rust 中,默认一切都是私有的 (private)

  • 父模块看不到子模块的内容,除非子模块加了 pub
  • 子模块可以看父模块的内容(子不嫌母丑?)。

3. 路径 (Paths)

要调用 add_to_waitlist,我们需要找到它的路径。

  • 绝对路径:从 Crate 根开始,以 crate 开头。 crate::front_of_house::hosting::add_to_waitlist();
  • 相对路径:从当前模块开始,以 selfsuper 开头。 front_of_house::hosting::add_to_waitlist();

4. use 关键字

每次都写全路径太累了,use 可以把路径引入当前作用域(建立软链接)。

1use crate::front_of_house::hosting;
2
3pub fn eat_at_restaurant() {
4    hosting::add_to_waitlist(); // 爽多了
5}

5. 文件分割

如果把所有代码都塞在 lib.rs 里,那跟塞在 main.rs 有什么区别? Rust 允许我们将模块提取到独立文件中。

写法一(推荐): src/lib.rs: mod front_of_house; (声明模块,内容去同名文件中找) src/front_of_house.rs: (这里写模块内容)

写法二(子目录): src/lib.rs: mod front_of_house; src/front_of_house/mod.rs: (这里写模块内容,这是老写法,现在依然支持但不如写法一清爽)

6. 二进制 Crate 引用 库 Crate

如果你的 Package 同时包含 src/main.rssrc/lib.rs。 在 main.rs 中使用 lib.rs 的内容,必须把它当成外部库来用。

假设 Package 名字叫 my_project。 在 main.rs 中: use my_project::front_of_house::hosting;

7. 小结

第十一篇笔记,也是 2025 年的第一篇。我们攻克了模块系统。

  • Package 是 Cargo 管理的。
  • Crate 是编译出的二进制或库。
  • mod 是代码的文件夹。
  • pub 是打开大门的钥匙(默认锁门)。
  • use 是方便引用的快捷方式。

当你被 Module not found 搞疯的时候,记得回来看看这层级关系。

下一篇,我们将学习 常用集合 (String, Vector, HashMap)。这些是应用开发中最离不开的数据结构。


练习题

  1. 尝试将 front_of_house 模块提取到单独的文件 src/front_of_house.rs 中,并在 lib.rs 中重新导出它。
  2. 创建一个包含私有字段的结构体,并尝试在另一个模块中修改该私有字段,观察编译错误。

思考题

Rust 的 mod 声明式系统(必须显式写 mod x; 才能加载文件)和 Go/Java/Python 的自动文件加载系统相比,有什么优缺点?


本文代码示例

关注公众号:极客老墨

更多 AI 应用开发、工程实践和效率工具分享,欢迎扫码关注。

极客老墨微信公众号二维码

相关阅读