Rust中模块定义与导入

模块(mod) 是核心的代码组织工具,用于解决命名冲突、控制代码可见性(封装)、拆分大型项目。

  1. 组织代码结构:把逻辑相关的函数、结构等放在一个模块中,保持代码清晰。
  2. 控制可见性:模块内的项(如函数、结构等)默认是私有的(private),根据需要设定其公开性。
  3. 命名空间管理(作用域隔离):模块支持层级路径访问,可防止命名冲突。

mod与use关系

术语本质作用
文件/目录作为 mod 名模块的「物理载体」(单文件 = 单个 mod,目录 = 模块容器)物理层面组织代码
明确用 mod <name> 声明模块的「编译器注册」(告诉编译器 “这个模块存在,加入项目”)逻辑层面注册,将其纳入 crate 的模块树
use 语句模块元素的「作用域导入」把已注册模块中的元素导入当前作用域,简化访问

定义

模块既可以内联定义(inline module),也可以放在独立文件/目录中。

Rust 项目的根模块由入口文件决定:

  • 二进制项目:入口是 src/main.rsmain.rs 就是根模块(crate)。
  • 库项目:入口是 src/lib.rslib.rs 是根模块(crate)。

所有模块(文件 / 目录)都需在根模块中通过 mod 注册,才能被访问。

内联模块

在同一个文件内定义模块;用 mod 模块名 { ... } 声明模块。

// 定义根模块下的模块 `math`
mod math {
    // 模块内的私有函数(默认私有,仅模块内可访问)
    fn square(x: i32) -> i32 {
        x * x
    }

    // 用 `pub` 暴露公共函数(外部可访问)
    pub fn add(a: i32, b: i32) -> i32 {
        a + b
    }

    // 嵌套模块(模块可嵌套)
    pub mod advanced {
        pub fn multiply(a: i32, b: i32) -> i32 {
            a * b
        }
    }
}

fn main() {
    // 访问公共函数:通过「模块路径」访问
    println!("1+2 = {}", math::add(1, 2)); // 输出:3

    // 访问嵌套模块的公共函数
    println!("3*4 = {}", math::advanced::multiply(3, 4)); // 输出:12

    // 错误:无法访问私有函数 `math::square`(未加 pub)
    // println!("5² = {}", math::square(5));
}

文件或目录模块

将模块拆分为多个文件 / 目录,核心规则:

  • 一个文件对应一个模块(如 math.rs 对应模块 math)。
  • 一个目录对应一个模块(如 utils/ 对应模块 utils),目录名即为模块名(Rust 1.32以前,需要目录下添加mod.rs 声明子模块)。
// 文件模块
src/
├── main.rs       # 根模块
└── math.rs       # 模块 math(与文件名一致)

// 目录模块
src/
├── main.rs
└── utils/        # 模块 utils(目录)
    ├── math.rs   # 子模块 utils::math
    └── game.rs   # 子模块 utils::game

可见性

默认是模块私有(仅当前模块及子模块可访问),通过 pub 扩展可见性,支持细粒度控制:

关键字可见范围适用场景
pub全局可见暴露公共 API
pub(crate)当前 crate 内所有地方可见内部模块间共享,不对外暴露
pub(super)当前模块的「父模块」及其后代模块中可见子模块向父模块暴露
pub(in 路径)在指定路径代表的模块及其所有后代中可见跨多级模块的有限共享
默认(无修饰)当前模块及其后代模块中可见
mod outer {
    pub fn a() { println!("a: 全局可见"); }
    pub(crate) fn b() { println!("b: 仅当前 crate 可见"); }
    pub(super) fn c() { println!("c: 仅父模块(根模块)可见"); }
    pub(in crate::outer::inner) fn d() { println!("d: 仅 inner 模块可见"); }

    mod inner {
        pub fn e() {
            // 子模块可访问父模块的所有元素(包括私有)
            super::a();
            super::b();
            super::c();
            super::d(); // 符合 pub(in crate::outer::inner) 的范围
        }
    }
}

fn main() {
    outer::a(); // 允许:pub 全局可见
    outer::b(); // 允许:pub(crate) 仅当前 crate 可见
    outer::c(); // 允许:pub(super) 父模块(根模块)可见
    // outer::d(); // 错误:d 仅 inner 模块可见

    outer::inner::e(); // 允许:e 是 pub 的
}

引用

引用模块内的元素时,需要通过路径(类似文件系统路径):

  • 绝对路径:从根模块(crate) 开始,以 crate:: 开头(crate 代表当前项目)。
  • 相对路径:从当前模块开始,用 self(当前模块,默认可省略)或 super(父模块)导航。

use关键字用于模块导入:

  • 导入多个元素:用大括号{}
  • 导入全部公共元素:通配符*
  • 别名as:也可解决冲突
  • 重导出:pub use 不仅导入元素,还将其重新暴露给外部,常用于简化 API 路径(隐藏内部层级)

注册与导入

当前项目内的源代码文件和内部模块,首次在项目中引入(use )时,必须用 mod 声明注册(在根模块(main.rs/lib.rs)写 mod <mod-name>;)。对于外部依赖(如 serdeprost),通过 Cargo.toml 声明(已在其自身的 lib.rs 中注册过了,无需 mod),直接用 use 导入即可。

  • 单文件 mod(如 engine.rs):在根模块(main.rs/lib.rs)写 mod engine;(注册);
  • 目录 mod(如 engine/):在根模块写 mod engine { pub mod core; }(注册);
  • 同一文件内的显式 mod:直接写 mod math { ... }(声明即注册)。

mod注册时,查找规则(以mod game;为例):

  1. 当前目录下的文件 game.rs
  2. 当前目录下的文件夹 game
  3. 一旦找到其中一个,就会停止继续查找。

注册、引用示例:

// 目录结构
src/
├── main.rs               # 根模块(注册所有自定义模块)
├── utils.rs              # 单文件模块:通用工具函数(简单逻辑)
└── calc/                 # 目录模块:计算核心(复杂逻辑,拆分子模块)
    ├── base.rs           # 子模块 calc::base:基础加减
    └── advanced.rs       # 子模块 calc::advanced:高级乘除/平方

// main.rs(根模块)
///////////////////////////////////////////////////////////////////////////
// 第一步:mod 注册(告诉编译器“这些模块存在,加入项目”)
// 注意:所有自定义模块(自己写的)都必须在这里注册,否则编译器找不到
///////////////////////////////////////////////////////////////////////////
mod utils; // 注册单文件模块 utils(对应 utils.rs)

// 注册目录模块 calc(对应 calc/ 目录),并声明其下的子模块
mod calc {
    pub mod base;    // 注册子模块 calc::base(对应 calc/base.rs)
    pub mod advanced;// 注册子模块 calc::advanced(对应 calc/advanced.rs)
}

///////////////////////////////////////////////////////////////////////////
// 第二步:use 导入(将已注册模块的元素导入当前作用域,简化访问)
///////////////////////////////////////////////////////////////////////////
// 场景1:导入单个元素(直接使用函数名)
use utils::trim_str;

// 场景2:导入模块(后续通过“模块名::元素”访问,适合模块内多个元素需使用)
use calc::base;

// 场景3:嵌套导入(一次性导入目录模块下的多个子模块元素)
use calc::advanced::{multiply, square_with_format};

// 场景4:别名(解决命名冲突,或简化长模块名/元素名)
use calc::advanced::square as calc_square;

fn main() {
    // #####################################################################
    // 演示1:不用 use 导入,直接通过“绝对路径”访问(证明 mod 注册后即可使用)
    // #####################################################################
    let raw_str = "  rust mod 示例  ";
    let trimmed = crate::utils::trim_str(raw_str); // 绝对路径:crate::utils::trim_str
    
    let add_res = crate::calc::base::add(10, 20); // 绝对路径:crate::calc::base::add
    
    // #####################################################################
    // 演示2:用 use 导入后访问(简化路径,适合频繁访问)
    // #####################################################################
    // 场景1:导入单个元素(直接用 trim_str,无需前缀)
    let trimmed2 = trim_str("  use 简化访问  ");

    // 场景2:导入模块(用“模块名::元素”访问,适合多个元素)
    let sub_res = base::subtract(50, 15); // 导入 base 模块,直接 base::subtract

    // 场景3:嵌套导入(直接用元素名,多个元素一次性导入)
    let mul_res = multiply(6, 7); // 直接用 multiply(来自 calc::advanced)
    let formatted_square = square_with_format(5); // 直接用 square_with_format

    // 场景4:别名(用 calc_square 替代 square,避免冲突)
    let square_res = calc_square(10); // 别名 calc_square 替代 square
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值