目录

📝 文章摘要

一、背景介绍

二、原理详解

2.1 Crate:编译单元

2.2 mod:定义模块

2.3 pub:可见性(Visibility)

2.4 use:导入路径

2.5 super 和 self

三、代码实战

3.1 实战:构建一个 my_lib 库

3.2 实战:`pub struct 的可见性

四、结果分析

4.1 模块树(Module Tree)可视化

五、总结与讨论

5.1 核心要点

5.2 讨论问题

参考链接


📝 文章摘要

Rust 的模块(Module)和包(Crate)系统是其强大封装性和可维护性的来源,但它也是新手(甚至是有经验的开发者)最容易混淆的概念之一。本文将深入剖析 Rust 的模块系统,厘清 Crate(包)、Module(模块)、mod 关键字(内联与文件)、use(导入)、super(父级)和 self(当前)的精确含义,以及 Rust 2018 引入的“路径清晰化”(Path Clarity)是如何简化模块管理的。


一、背景介绍

C/C++ 使用 #include(头文件)来组织代码,这本质上是“文本复制”,容易导致命名空间污染和极长的编译时间。Java/Python 使用“文件即模块”的系统,相对清晰,但缺乏对“私有性”的精细控制。

Rust 的模块系统是一个显式(Explicit)的、树状(Hierarchical)的系统。它强制你明确声明:

  1. Crate(:Rust 的编译单元(lib 或 bin)。
  2. Module(模块):Crate 内部的命名空间(Namespace)和私有性边界(Privacy Boundary)。
  3. 路径(Path):如何从 Crate 根(`crate::)找到一个 Item。

理解“模块树”(Module Tree)是理解 Rust 编译的第一步。

二、原理详解

2.1 Crate:编译单元

一个 Crate 是最小的编译单元。

  • Binary Crate (二进制包):`src/main.s` 是它的 Crate 根(Crate Root)
  • Library Crate (库包):`src/librs` 是它的 Crate 根

Crate 根(main.rs 或 lib.rs)是编译器查找模块树的起点。

2.2 mod:定义模块

mod 关键字定义一个模块。它有两种形式:

1. 内联(Inline)模块:

// src/lib.rs (Crate Root)

// 定义 `front_of_house` 模块
mod front_of_house {
    // 定义 `hosting` 子模块
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

2. 文件(File)模块 (Rust 20+):

这是 Rust 2018 之后推荐的方式,更简洁。

文件结构:

src/
├── lib.rs
└── front_of_house/
    ├── hosting.rs
    └── mod.rs

src/lib.rs (Crate Root)

// 告诉 Rust 在 "src/front_of_house.rs" 
// 或 "src/front_of_house/mod.rs" 中查找内容
pub mod front_of_house; 

src/front\_of\_housers (模块根)

// 告诉 Rust 在 "src/front_of_house/hosting.rs" 
// 中查找 `hosting` 模块的内容
pub mod hosting; 

src/front\_of\_house/ng.rs

pub fn add_to_waitlist() {}

2.3 pub:可见性(Visibility)

Rust 中的所有 Item(函数、结构体、模块等)默认是私有(Private) 的。

  • 私有(n`):只能被当前模块及其子模块访问。
  • 公共(pub fn:可以被外部模块访问。

pub(crate) 和 pub(super)
pub 还可以是受限的:

  • `pub(crate):只在当前 Crate 内可见(对库的内部实现很有用)。
  • pub(super):只在父模块(super)可见。

2.4 use:导入路径

mod 定义了模块树,use 则创建了“快捷方式”(Shortcuts)。

use 总是从 Crate 根(或 self / super)开始。

// src/lib.rs
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

// 绝对路径 (Absolute Path)
// `crate` 关键字代表当前 Crate 的根
use crate::front_of_house::hosting;

// 相对路径 (Relative Path)
// mod back_of_house {
//     use super::front_of_house::hosting;
// }

pub fn eat_at_restaurant() {
    // `use` 之后,我们可以使用 `hosting` 快捷方式
    hosting::add_to_waitlist();
}

2.5 super 和 self

  • self:指向当前模块。
  • super:指向父模块。
mod outer {
    fn private_fn() {}
    
    mod inner {
        fn inner_fn() {
            // 相对路径:调用同级
            self::other_inner_fn(); 
            // 相对路径:调用父级
            super::private_fn();
        }
        
        fn other_inner_fn() {}
    }
}

三、代码实战

3.1 实战:构建一个 my_lib 库

我们将构建一个库,其文件结构如下,并演示如何正确地组织 mod 和 use

文件结构:

my_lib/
├── Cargo.toml
└── src/
    ├── lib.rs         (Crate Root)
    ├── network/
    │   ├── client.rs  (mod client)
    │   └── mod.rs     (mod network)
    └── utils.rs       (mod utils)

src/lib.rs (Crate Root)

// 1. 声明 `network` 模块。
//    Rust 会查找 "src/network.rs" 或 "src/network/mod.rs"
pub mod network;

// 2. 声明 `utils` 模块。
//    Rust 会查找 "src/utils.rs"
pub mod utils;

// 3. 将 `network::client` 中的 `connect` 函数
//    "提升" (re-export) 到库的顶层。
//    外部用户现在可以调用 `my_lib::connect()`
pub use crate::network::client::connect;

pub fn main_api_call() {
    // 使用绝对路径
    crate::network::client::connect();
    
    // 使用相对路径 (因为 utils 在 lib.rs 中声明)
    utils::helper();
}

srctwork/mod.rs (network 模块)

// 声明 `client` 子模块。
// Rust 会查找 "src/network/client.rs"
pub mod client;

// (我们也可以在这里定义 network 相关的函数)
fn internal_network_fn() {
    // ...
}

src/network/client.rs (client 模块)

pub fn connect() {
    println!("Connecting to network...");
    
    // 访问同级模块 (network::internal_network_fn)
    // 必须使用 `super` (父级)
    super::internal_network_fn();
    
    // 访问 Crate 根的 `utils` 模块
    // 必须使用 `crate`
    crate::utils::helper();
}

src/utils.rs (utils 模块)

pub fn helper() {
    println!("Utils helper called.");
}

3.2 实战:`pub struct 的可见性

pub 的粒度是细致的。

mod my_mod {
    #[derive(Debug)]
    pub struct MyStruct {
        pub public_field: i32,
        private_field: i32, // 默认私有
    }

    impl MyStruct {
        pub fn new() -> Self {
            Self { public_field: 1, private_field: 10 }
        }
    }
}

fn main() {
    let mut s = my_mod::MyStruct::new();
    
    // ✓ OK: 字段是 pub 的
    s.public_field = 100;
    
    // ❌ 编译错误:field `private_field` is private
    // s.private_field = 200; 

    // ❌ 编译错误:`private_field` 是私有的,
    // 我们无法在模块外直接构造
    // let s2 = my_mod::MyStruct {
    //     public_field: 1,
    //     private_field: 2,
    // };
}

pub struct 并不意味着其所有字段都是 pub。这种封装性(Encapsulation)强制用户使用 MyStruct::new()(构造函数)来创建实例,确保了数据的不变性(Invariants)。


四、结果分析

4.1 模块树(Module Tree)可视化

我们实战中的 my_lib Crate,其“模块树”在编译时是这样的:

在这里插入图片描述

Rust 2018 vs 2015
在 Rust 2015(旧版)中,路径解析是混乱的。use foo 既可能指 Crate 根的 foo,也可能指当前模块的 foo

Rust 2018 路径清晰化(Path Clarity)规定:

  • use some::path总是从 C Crate 根(crate::)开始(除非是 self:: 或 super::)。
  • 解决了所有歧义。-----

五、总结与讨论

5.1 核心要点

  • Crate 根src/ain.rssrc/lib.rs,是模块树的起点。
  • mod foo;声明一个模块,告诉编译器在 src/foo.rs 或 src/foo/mod.rs 中查找。
  • mod foo... }内联定义一个模块。
  • use:创建路径的“快捷方式”,影响模块树或私有性。
  • 默认私有:Rust 的一切默认都是私有的。pub 用于暴露 API。
  • 路径crate::(绝对路径)、super::(父级)、self::(当前)是访问模块树的锚点。

5.2 讨论问题

  1. 为什么 Rust 2018 倾向于使用 src/oo.rs而不是src/foo/mod.rs?(提示:mod.rs 在 IDE 标签页中难以区分)
  2. use 的“提升”(Re-exporting)——pub use crate::internal::MyType;`——在设计库 API 时有何重要意义?
  3. “Crate” 和 “Package” 在 Cargo.toml 的上下文中有什么区别?(提示:一个 Package 可以包含多个 Crate)

参考链接

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐