rust 模块管理

144 阅读3分钟

模块

每个 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 上的依赖。

cargo 工作空间

Cargo 工作空间 - Rust 程序设计语言 中文版 (rustwiki.org)