
模块系统:代码界的"收纳术",让混乱变秩序
想象一下,你的代码文件越来越长,从 100 行到 500 行,再到 1000 行...最后打开文件,好家伙,滚轮都滚不到底。找个函数得用 Ctrl+F,改个变量得小心翼翼怕重名。
这时候你就需要模块系统了。模块就像是你家的收纳柜:衣服放衣柜,书放书架,厨房用品放橱柜。找东西时,直接去对应的柜子拿,不用翻箱倒柜。
Rust 的模块系统被很多人吐槽"复杂",但一旦你搞懂了,就会发现它其实很有逻辑。今天咱们就来拆解这个"复杂"的系统,让你从此不再迷路。
模块(Module)就是代码的组织单元。它帮你:
生活化类比:
Crate 是 Rust 编译的最小单元。有两种:
main 函数main 函数// src/main.rs - 二进制 crate
fn main() {
println!("我是可执行程序!");
}
// src/lib.rs - 库 crate
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
Package 是 Cargo 管理的单位,一个 Package 可以包含:
判断方法:看 Cargo.toml 文件。有 [package] 就是 Package。
# Cargo.toml
[package]
name = "my_project"
version = "0.1.0"
edition = "2021"
# 这是一个 Package
mod 声明模块用 mod 关键字声明。有两种方式:
方式 1:内联模块(适合小模块)
mod garden {
fn plant() {
println!("种花🌸");
}
}
方式 2:文件模块(适合大模块)
// src/main.rs
mod garden; // Rust 会去找 src/garden.rs 或 src/garden/mod.rs
// src/garden.rs
fn plant() {
println!("种花🌸");
}
Rust 用路径来定位模块里的东西,就像文件系统的 /home/user/file.txt。
路径类型 | 语法 | 说明 |
|---|---|---|
绝对路径 | crate::module::item | 从 crate 根开始 |
相对路径 | self::item 或 super::item | 从当前模块开始 |
使用声明 | use crate::module::item | 引入到当前作用域 |
// 绝对路径
crate::garden::plant();
// 相对路径(当前模块)
self::plant();
// 相对路径(父模块)
super::other_function();
pub 关键字默认情况下,模块里的东西都是私有的(只有自己模块能用)。想让外面用?加 pub!
// src/lib.rs
pub mod utils {
pub fn helper() { // 外部可以调用
println!("我是公共函数");
}
fn internal() { // 外部不能调用
println!("我是私有函数");
}
}
// src/main.rs
use my_lib::utils;
fn main() {
utils::helper(); // ✅ 可以
utils::internal(); // ❌ 编译错误
}
可见性层级:
pub - 完全公开pub(crate) - 整个 crate 内可见pub(super) - 父模块可见pub(in path) - 指定路径可见use:引入模块use 关键字用来把模块里的东西"引入"到当前作用域,这样就不用写长长的路径了。
// 不使用 use
fn main() {
std::collections::HashMap::new();
std::collections::HashMap::new();
std::collections::HashMap::new();
}
// 使用 use
use std::collections::HashMap;
fn main() {
HashMap::new();
HashMap::new();
HashMap::new();
}
use 的骚操作:
// 引入多个
use std::collections::{HashMap, HashSet};
// 重命名(避免冲突)
use std::fmt::Result as FmtResult;
use std::io::Result as IoResult;
// 引入所有(慎用!)
use std::collections::*;
// 嵌套路径
use std::{collections::HashMap, io::Write};
// src/main.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("已加入等候名单");
}
pub fn seat_at_table() -> String {
String::from("table 1")
}
}
mod serving {
fn take_order() {}
fn serve_order() {}
}
}
fn main() {
// 使用绝对路径
front_of_house::hosting::add_to_waitlist();
// 使用 use 引入
use front_of_house::hosting;
hosting::seat_at_table();
}
// src/main.rs
mod front_of_house {
mod hosting { // 没有 pub!
pub fn add_to_waitlist() {}
}
}
fn main() {
front_of_house::hosting::add_to_waitlist();
}
编译器错误:
error[E0603]: module `hosting` is private
--> src/main.rs:9:21
|
9 | front_of_house::hosting::add_to_waitlist();
| ^^^^^^^ private module
|
note: the module `hosting` is defined here
--> src/main.rs:3:5
|
3 | mod hosting {
| ^^^^^^^^^^^
人话翻译:编译器:"hosting 模块是私有的,外面不让进!想让人家用,加个 pub 啊!"
// src/main.rs
mod garden {
fn plant() {}
}
fn main() {
plant(); // 错了!plant 在 garden 模块里
}
编译器错误:
error[E0425]: cannot find function `plant` in this scope
--> src/main.rs:8:5
|
8 | plant();
| ^^^^^ not found in this scope
|
help: consider importing this function
|
1 | use crate::garden::plant;
|
人话翻译:编译器:"plant 不在这里!它在 garden 模块里。要么写全路径 garden::plant(),要么用 use 引进来。"
// src/a.rs
use crate::b::func_b;
pub fn func_a() {
func_b();
}
// src/b.rs
use crate::a::func_a;
pub fn func_b() {
func_a(); // 编译错误:循环依赖
}
编译器错误:
error[E0423]: expected function, found module
--> src/b.rs:6:5
|
6 | func_a();
| ^^^^^^ not a function
人话翻译:编译器:"你俩互相调用,是想把我绕晕吗?重构一下代码结构吧!"
pubmod utils {
fn helper() {} // 私有
}
fn main() {
utils::helper(); // 编译错误
}
修复:加 pub。
mod utils {
pub fn helper() {} // 公开
}
// src/main.rs
mod garden; // Rust 会找 src/garden.rs
// 但你把文件放到了 src/modules/garden.rs ❌
修复:文件要放在正确位置。
src/
├── main.rs
├── garden.rs ✅ 正确
└── modules/
└── garden.rs ❌ 错误
use 引入后还是用错use crate::front_of_house::hosting;
fn main() {
hosting::add_to_waitlist(); // ✅ 对
front_of_house::hosting::add_to_waitlist(); // ✅ 也对
}
记住:use 只是引入别名,原来的路径还能用。
super 用晕了mod front {
mod hosting {
fn add_to_waitlist() {
super::serve(); // 指向 front 模块
}
}
fn serve() {}
}
记忆技巧:
self = 当前目录(.)super = 上一级目录(..)crate = 项目根目录(/)// src/lib.rs
pub mod calculator {
pub mod arithmetic {
pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn subtract(a: i32, b: i32) -> i32 { a - b }
pub fn multiply(a: i32, b: i32) -> i32 { a * b }
pub fn divide(a: i32, b: i32) -> Option<i32> {
if b == { None } else { Some(a / b) }
}
}
pub mod statistics {
pub fn average(nums: &[i32]) -> f64 {
let sum: i32 = nums.iter().sum();
sum as f64 / nums.len() as f64
}
}
}
// src/main.rs
use my_lib::calculator::arithmetic;
use my_lib::calculator::statistics;
fn main() {
let sum = arithmetic::add(, );
let avg = statistics::average(&[, , , , ]);
println!("和:{}, 平均:{}", sum, avg);
}
my_project/
├── Cargo.toml
└── src/
├── main.rs
├── models.rs
├── views.rs
└── controllers.rs
// src/main.rs
mod models;
mod views;
mod controllers;
use controllers::handle_request;
fn main() {
handle_request();
}
// src/models.rs
pub struct User {
pub name: String,
pub age: u32,
}
// src/views.rs
use crate::models::User;
pub fn render_user(user: &User) {
println!("用户:{}, 年龄:{}", user.name, user.age);
}
// src/controllers.rs
use crate::models::User;
use crate::views::render_user;
pub fn handle_request() {
let user = User {
name: String::from("Alice"),
age: ,
};
render_user(&user);
}
use 的最佳实践// ❌ 不推荐:引入所有
use std::*;
// ✅ 推荐:明确引入
use std::collections::HashMap;
use std::io::{self, Write};
// ✅ 推荐:重命名避免冲突
use std::result::Result as StdResult;
use my_lib::Result;

pub——这是 Rust 的封装哲学crate:: 从头开始,self:: 当前,super:: 父级use 引入到当前作用域,可以重命名避免冲突mod foo 对应 src/foo.rs 或 src/foo/mod.rs下篇预告:模块里放什么?函数?数据?下一篇咱们聊聊结构体,看看 Rust 怎么组织数据!