模块与包管理
这篇讲 Rust 的模块系统——如何用 mod、use、pub 组织代码,以及 Cargo 如何管理依赖和项目配置。学会了这个,你就能写出结构清晰的 Rust 项目。
Crate 与 Package
- Package(包):一个 Cargo 项目——包含一个
Cargo.toml和若干 crate - Crate(箱子):编译的最小单位,分两种:
- binary crate:生成可执行文件,入口是
src/main.rs - library crate:生成库文件,入口是
src/lib.rs
- binary crate:生成可执行文件,入口是
my-project/
Cargo.toml
src/
main.rs # binary crate 根
lib.rs # library crate 根(如果需要发布库)一个 package 可以包含多个 binary crate(放在 src/bin/ 下)。
Cargo.toml 配置
[package]
name = "my-app"
version = "0.1.0"
edition = "2024"
[dependencies]
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
rand = "0.8"
[dev-dependencies]
criterion = "0.5"
[profile.release]
opt-level = 3
lto = true
codegen-units = 1edition = "2024" 是 Rust 最新的版本。edition 不是独立版本号——即使使用 edition 2024,编译器的版本(如 rustc 1.91.0)仍然独立。edition 只影响语法和某些语言语义的向后不兼容变化。模块(mod)
模块让你将代码组织在命名空间中:
// src/lib.rs
mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}
mod serving {
fn take_order() {}
fn serve_order() {}
}
}默认情况下,模块和其中的项都是私有的。用 pub 关键字暴露:
mod front_of_house {
pub mod hosting { // 模块本身是公开的
pub fn add_to_waitlist() {} // 函数也是公开的
}
}
pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径
front_of_house::hosting::add_to_waitlist();
}use 关键字
use 把路径引入作用域,避免重复写完整路径:
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}
// 重新导出——让外部也能使用
pub use crate::front_of_house::hosting;惯用法:
// 函数:use 到父模块层级(保留上下文提示)
use crate::front_of_house::hosting;
hosting::add_to_waitlist(); // 一眼看出是 hosting 模块的函数
// 结构体/枚举/trait:use 到自身
use std::collections::HashMap;
use std::io::Result as IoResult; // 遇到同名可以用 as 重命名
let map = HashMap::new();多文件模块组织
模块可以和文件系统对应:
src/
main.rs
lib.rs
front_of_house/
mod.rs # front_of_house 模块根
hosting.rs # front_of_house::hosting
serving.rs # front_of_house::serving或者(Rust 2018 edition 起推荐的新风格):
src/
main.rs
lib.rs
front_of_house.rs # front_of_house 模块
front_of_house/
hosting.rs # front_of_house::hosting 子模块
serving.rs # front_of_house::serving 子模块引入外部 Crate
在 Cargo.toml 添加依赖后,用 use 引入:
// 引入 serde 的 Serialize 和 Deserialize
use serde::{Serialize, Deserialize};
// 引入 rand 的全部内容
use rand::Rng;
#[derive(Serialize, Deserialize, Debug)]
struct User {
name: String,
age: u8,
}
fn main() {
let secret = rand::thread_rng().gen_range(1..=100);
println!("随机数: {secret}");
}常用第三方 Crate
| Crate | 用途 | 说明 |
|---|---|---|
serde + serde_json | 序列化 | Rust 事实上的序列化标准 |
tokio | 异步运行时 | 异步网络应用首选 |
reqwest | HTTP 客户端 | 简单易用的 HTTP 客户端 |
clap | CLI 参数解析 | 命令行工具标配 |
anyhow / thiserror | 错误处理 | 简化 Result 使用 |
log + env_logger | 日志 | 标准化日志方案 |
sqlx | 数据库 | 编译期 SQL 检查的 ORM |
rayon | 并行迭代 | 一行代码变并行 |
一句话小结
模块系统(mod / use / pub)管理代码组织,Cargo 管理依赖和构建。Rust 的包管理体验是编译语言中最好的——一个 Cargo.toml 搞定一切,没有 CMake 的折磨。下一篇讲 测试。
练习
- 创建一个 library crate,包含模块
math::operations,其中有一个公开函数add(a: i32, b: i32) -> i32。在lib.rs中通过pub use重新导出。 - 修改
Cargo.toml,添加rand作为依赖,然后用cargo build验证。
参考答案
- 文件结构:
src/
lib.rs
math/
mod.rs (或 math.rs + math/operations.rs)
operations.rssrc/math/operations.rs:
pub fn add(a: i32, b: i32) -> i32 {
a + b
}src/math/mod.rs:
pub mod operations;src/lib.rs:
mod math;
pub use math::operations;Cargo.toml添加:
[dependencies]
rand = "0.8"最后更新于