模块
每个 Rust 程序或者库都叫 crate。
每个 crate 都是由模块的层次结构组成。
每个 crate 都有一个根模块。
模块里面可以有全局变量、全局函数、全局结构体、全局 Trait 甚至是全局模块!
在 Rust 中,文件与模块树的层次结构并不是一对一的映射关系。我们必须在我们的代码中手动构建模块树。
可执行程序
根模块需要在一个叫main.rs 的文件里
库
根模块需要在一个叫lib.rs 的文件
创建模块
在 Rust 中,有两种方式来声明一个模块。例如,模块 foo 可以表示为:
- 一个名为
foo.rs的文件。 - 在名为
foo的目录,里面有一个叫mod.rs文件。
模块层次结构
模块可以互相依赖。要建立一个模块和其子模块之间的关系,你需要在父模块中这样写:
mod foo;
上面的声明将使编译器寻找一个名为 foo.rs或 foo/mod.rs 的文件,并将其内容插入这个作用域内名为 foo 的模块中。
内联模块
一个子模块可以直接内联在一个模块的代码中。
内联模块最常见的用途是创建单元测试。 下面我们创建一个只有在使用 Rust 进行测试时才会存在的内联模块!
// 当 Rust 不在测试模式时,这个宏会删除这个内联模块。
#[cfg(test)]
mod tests {
// 请注意,我们并不能立即获得对父模块的访问。我们必须显式地导入它们。
use super::*;
... 单元测试写在这里 ...
}
模块内部引用
你可以在你的 use 路径中使用如下 Rust 关键字来获得你想要的模块:
crate- 你的 crate 的根模块super- 当前模块的父模块self- 当前模块
导出
默认情况下,模块的成员不能从模块外部访问(甚至它的子模块也不行!)。 我们可以使用 pub 关键字使一个模块的成员可以从外部访问。
默认情况下,crate 中的成员无法从当前 crate 之外访问。我们可以通过在根模块中 (lib.rs 或 main.rs), 将成员标记为 pub 使它们可以访问。
结构体可见性
就像函数一样,结构体可以使用 pub 声明它们想要在模块外暴露的东西
// SeaCreature 结构体在我们的模块外面也能使用了
pub struct SeaCreature {
pub animal_type: String,
pub name: String,
pub arms: i32,
pub legs: i32,
// 我们把武器信息保密起来好了
weapon: String,
}
Prelude
你可能很好奇,为什么我们在没用 use 导入 Vec 或 Box 的情况下却可以到处使用它们。 这是因为标准库中有一个叫 prelude 的模块。
要知道,在 Rust 标准库中,以 std::prelude::* 导出的任何东西都会自动提供给 Rust 的各个部分。 Vec 和 Box 便是如此,并且其他东西(Option、Copy 等)也是如此。
常见的rust项目结构
my_project/
│
├── Cargo.toml # 项目配置文件
├── Cargo.lock # 依赖项锁定文件
│
├── src/ # 源代码目录
│ ├── lib.rs # 库模块根文件(如果是库项目)
│ ├── main.rs # 主程序文件(如果是二进制项目)
│ └── ... # 其他模块文件
│
├── tests/ # 集成测试
│ ├── integration/ # 集成测试目录
│ └── ... # 其他测试目录
│
├── benches/ # 基准测试
│ └── ... # 基准测试文件
│
└── examples/ # 示例项目
└── ... # 示例项目文件
除了这个基本结构,一些项目可能还会有其他目录,例如:
- docs/ : 项目文档,通常用于存放 Markdown 文件或其他文档资源。
- scripts/ : 包含用于自动化任务的脚本,如构建脚本、安装脚本等。
- vendor/ : 包含第三方依赖的本地副本,有时用于包含那些不在 crates.io 上的依赖。