Rust 模块与 Crate
这是系列最后一篇,讲 Rust 如何组织大型项目的代码。前面所有概念(函数、结构体、枚举、Trait)都在单个文件里讲,真实项目会有几十上百个文件。Crate、Package、Module 是 Rust 的代码组织系统,理解它才能写出可维护的大型项目。
1. 概念层次:Crate / Package / Module
这三个概念从大到小,新手容易混。理清它们:
// Rust 代码组织系统的几个概念,从大到小:
// 1. Crate(木箱):Rust 的【编译单元】
// 分两种:
// - binary crate(可执行程序):有 main 函数
// - library crate(库):供其他 crate 引用,有 lib.rs
// 2. Package(包):一个 Cargo 项目
// 包含一个 Cargo.toml + src/ 目录
// 一个 package 可以同时包含 binary 和 library
// 3. Module(模块):crate 内部的代码组织
// 用 mod 关键字声明,形成树形结构
// 例:一个典型的项目结构
// my_app/ <- package
// ├── Cargo.toml
// ├── src/
// │ ├── main.rs <- binary crate 的根
// │ ├── lib.rs <- library crate 的根(可选)
// │ ├── network/ <- 模块目录
// │ │ ├── mod.rs <- 模块声明
// │ │ ├── server.rs
// │ │ └── client.rs
// │ └── utils.rs <- 单文件模块
// └── tests/层次关系:
- Crate(编译单元):最小的独立编译单位。分 binary(可执行,有 main)和 library(库,供他人引用)。
- Package(包):一个 Cargo 项目,包含 1 个或多个 crate。由 Cargo.toml 描述。
- Module(模块):crate 内部的代码组织,用 mod 声明,形成树形结构。
2. mod 声明模块
用 mod 关键字声明模块。Rust 会按约定找对应的文件:
// src/main.rs 或 src/lib.rs
// 声明模块:mod 关键字
// Rust 会去找对应的文件:
// mod network; -> src/network.rs 或 src/network/mod.rs
mod network;
mod utils;
fn main() {
// 用路径访问模块里的内容
// 完整路径:crate::network::server::start()
network::server::start();
utils::helper();
}
// 内联模块:直接在文件里写
mod math {
// pub 表示公开(模块外可见)
// 默认是私有(只能在当前模块及子模块访问)
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
fn secret() -> i32 { // 私有,模块外不能用
42
}
// 嵌套子模块
pub mod geometry {
pub fn area(r: f64) -> f64 {
3.14 * r * r
}
}
}
fn use_math() {
// 完整路径访问
let sum = math::add(1, 2);
let a = math::geometry::area(5.0);
}模块文件查找规则(Rust 2018 之后):
- mod network; 在 main.rs/lib.rs 里:找
src/network.rs或src/network/mod.rs。 - mod server; 在 network/mod.rs 里:找
src/network/server.rs或src/network/server/mod.rs。 - 内联模块:直接在文件里写
mod foo加花括号体,不对应文件。
可见性:默认一切都是私有的。要让模块外能访问,加 pub。这是 Rust 的安全设计——不主动暴露内部实现。
3. use 导入路径
用 use 把长路径"导入"到当前作用域,避免每次写全路径:
mod network;
// use:把路径"导入"到当前作用域,简化引用
use network::server; // 之后可以直接写 server::start()
use network::server::start; // 直接导入函数
fn main() {
server::start(); // 不用写 network:: 前缀
start(); // 直接调用
// 嵌套路径一次导入多个
use network::{server, client};
server::start();
client::connect();
// 用 as 重命名(避免命名冲突)
use network::server as srv;
srv::start();
// 通配 * 导入全部(谨慎用,易污染命名空间)
// use network::*;
}
// 标准库的 prelude:Rust 自动导入一批最常用项
// 如 Vec、String、Option、Result、Vec、Box 等
// 不需要 use 就能用,因为它们在 prelude 里几个技巧:
- 嵌套导入:用
use把多个路径放在花括号里一次导入。 - 重命名:用
as关键字给导入项起别名,避免命名冲突,例如把 io 模块的 Result 重命名为 IoResult。 - 通配 *:导入全部,但易污染命名空间,谨慎用。
- prelude:Rust 自动导入一批最常用的项(Vec、String、Option、Result 等),不用 use 就能用。
4. pub 可见性
Rust 的可见性规则:默认私有,显式 pub 公开。这是封装的基石:
// 可见性规则:默认一切都是私有的!
// pub 关键字让项公开
// 文件结构:
// src/network/mod.rs
pub mod server; // 公开子模块(让外部能 use network::server)
pub mod client;
mod internal; // 私有子模块(只在 network 内部用)
pub fn connect() { // 公开函数
// ...
}
fn helper() { // 私有函数
// ...
}
// 结构体的可见性:字段默认私有
pub struct User {
pub name: String, // 公开字段
age: u32, // 私有字段(模块外不能直接访问)
}
// 枚举:一旦枚举 pub,所有变体都公开
pub enum Color {
Red, Green, Blue, // 全部公开
}
// 三种路径前缀:
// crate::xxx 从当前 crate 根开始(绝对路径)
// self::xxx 从当前模块开始
// super::xxx 上一级模块(类似 ../ )
// ::xxx 外部 crate 或标准库关键点:
- 默认私有:不加 pub 的项,只能在当前模块及子模块访问。这强制你显式暴露 API。
- 结构体字段独立可见:struct 标 pub 后,字段仍是私有的。要暴露字段,需单独标 pub。
- 枚举变体跟随枚举:enum 标 pub 后,所有变体都公开(和 struct 相反)。
- 路径前缀:crate::(绝对)、self::(当前)、super::(上级)。
5. 引用第三方 Crate
Rust 的官方 crate 仓库叫 crates.io,所有开源库都在那里。用 Cargo 添加依赖:
// Cargo.toml
// [dependencies]
// rand = "0.8" # 引入第三方库 rand
// serde = { version = "1", features = ["derive"] }
// 在代码里 use 外部 crate
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let n: i32 = rng.gen_range(1..=100); // 1-100 随机数
println!("随机数: {}", n);
}
// 添加依赖的几种方式:
// 1. cargo add rand # 自动写入 Cargo.toml 并下载
// 2. 手动编辑 Cargo.toml
// 3. cargo add rand@0.8 # 指定版本
// 版本号语法(SemVer):
// "1.2.3" 精确版本
// "1.2" 允许 1.2.x
// "1" 允许 1.x.x
// "^1.2.3" 允许 1.x.x(默认,和 "1" 类似)
// "=1.2.3" 精确版本
// "*" 任意版本(危险!)常用操作:
- cargo add rand:最方便,自动写入 Cargo.toml 并下载。
- cargo update:更新依赖到 Cargo.toml 允许的最新版。
- cargo tree:查看依赖树。
- crates.io:浏览搜索第三方库,看下载量、文档、最新版本。
Rust 的生态远比想象丰富:Web 框架(axum, actix-web)、序列化(serde)、数据库(sqlx, diesel)、异步(tokio)、HTTP(reqwest, hyper)、命令行(clap)……几乎所有需求都有成熟的 crate。
6. 标准库 std
标准库 std 是 Rust 自带的,不需要在 Cargo.toml 声明,直接 use 即可。它提供基础数据结构和 IO:
use std::collections::HashMap;
use std::fs;
use std::io::Read;
fn main() {
// std 是 Rust 标准库,无需在 Cargo.toml 声明
// 直接 use 即可
// 常用标准库模块:
// std::collections - HashMap, HashSet, BTreeMap, VecDeque
// std::fs - 文件操作
// std::io - 输入输出
// std::net - 网络编程
// std::thread - 多线程
// std::sync - 同步原语(Mutex, Arc, RwLock)
// std::time - 时间(Duration, Instant)
// std::env - 环境变量、命令行参数
// std::process - 进程控制
// std::path - 路径处理(跨平台)
let mut map = HashMap::new();
map.insert("apple", 3);
map.insert("banana", 5);
println!("{:?}", map);
// 读取环境变量
if let Ok(name) = std::env::var("USER") {
println!("用户: {}", name);
}
}标准库的核心模块:
- std::collections:HashMap、HashSet、BTreeMap、VecDeque。
- std::fs / std::io:文件和输入输出。
- std::net:TCP/UDP 网络编程。
- std::thread / std::sync:多线程与同步(Mutex、Arc)。
- std::time:时间(Duration、Instant)。
- std::env / std::process:环境变量、进程控制。
- std::path:跨平台路径处理。
7. 实战项目结构示例
看一个典型的中型应用如何组织模块:
// 项目结构示例:一个完整应用的模块组织
// src/main.rs
mod config; // 引入 config.rs
mod db; // 引入 db/mod.rs
mod api; // 引入 api/mod.rs
mod utils; // 引入 utils.rs
use config::Config;
use db::Database;
fn main() {
let cfg = Config::load();
let db = Database::new(&cfg.db_url);
api::serve(db);
}
// src/config.rs
pub struct Config { /* ... */ }
impl Config {
pub fn load() -> Config { /* ... */ }
}
// src/db/mod.rs
pub mod models;
pub mod queries;
pub struct Database { /* ... */ }
// src/api/mod.rs
pub fn serve(db: db::Database) { /* ... */ }
// 这种组织让代码可维护,不同关注点分到不同模块组织原则:
- 按关注点分模块:config、db、api、utils 各管一摊。
- lib + main 分离:lib.rs 放业务逻辑,main.rs 只做命令行处理。
- 子模块细化:db 下面再分 models、queries。
- pub 只暴露必要 API:内部实现私有,降低耦合。
8. workspace(多 crate 项目)
大型项目可能有多个相关 crate(如一个核心库 + 多个工具)。Cargo workspace 让多个 crate 共享同一个 Cargo.lock 和 target 目录:
- 根目录 Cargo.toml 写
[workspace] members = ["core", "cli", "server"]。 - 每个 member 是独立的 package(自己的 Cargo.toml + src)。
- 它们之间用 path 依赖互相引用,如把 core 字段指向同工作区里的 core 目录。
- 共享依赖版本(避免重复编译),CI 更高效。
这是 Rust 大型项目(如 Rust 编译器自身、tokio)的标准组织方式。
9. 文档注释与 cargo doc
Rust 的文档注释(///)会被 cargo doc 生成漂亮的 HTML 文档站点。这是工程化的亮点:
- 每个 pub 项都应该写文档注释(markdown 格式)。
- 文档注释里可以写代码示例,
cargo test会自动运行它们作为测试(叫 doctest)。 - 标准库文档 doc.rust-lang.org 就是这样生成的。
- 第三方 crate 文档在 docs.rs 自动生成。
小结
这一篇你掌握了 Rust 的代码组织系统:Crate(编译单元,binary/library)、Package(Cargo 项目)、Module(mod 声明)、use(导入路径)、pub(可见性)、第三方 crate(crates.io + cargo add)、workspace(多 crate 项目)。这套系统让你能组织任意大的 Rust 项目,同时保持模块化和封装。
系列总结
恭喜!你完成了 Rust 16 篇入门系列。回顾这段旅程:从环境安装到所有权、借用,再到结构体、枚举、Trait、错误处理、模块系统——你已经掌握了 Rust 的全部核心概念。
Rust 的入门确实比 Python 难,你一定在所有权和借用那里撞过墙——这是所有Rust 学习者的必经之路。但跨过这道墙后,你得到的回报是:极少 bug 的代码、极致的性能、以及一种全新的编程思维方式。
下一步建议:
- 读 The Book:doc.rust-lang.org/book,官方权威教程,免费,有中文翻译。
- 做 Rustlings:github.com/rust-lang/rustlings,一系列小练习,巩固语法。
- 动手写项目:命令行工具是 Rust 的甜点区。写一个 TODO CLI、文件查找器、markdown 转 HTML。
- 进阶方向:Web 后端(axum / actix-web)、异步(tokio)、WebAssembly(wasm-bindgen)、嵌入式。
- 加入社区:Rust 中文社区、Reddit r/rust、官方 Discord,友善且活跃。
Rust 连续多年"最受喜爱"不是没有理由。慢慢来,值得。祝你在 Rust 的旅程中收获满满!
← 上一篇 Rust 错误处理
返回 Rust 教程目录