跳至内容

模块与包管理

这篇讲 Rust 的模块系统——如何用 modusepub 组织代码,以及 Cargo 如何管理依赖和项目配置。学会了这个,你就能写出结构清晰的 Rust 项目。

Crate 与 Package

  • Package(包):一个 Cargo 项目——包含一个 Cargo.toml 和若干 crate
  • Crate(箱子):编译的最小单位,分两种:
    • binary crate:生成可执行文件,入口是 src/main.rs
    • library crate:生成库文件,入口是 src/lib.rs
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 = 1
edition = "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异步运行时异步网络应用首选
reqwestHTTP 客户端简单易用的 HTTP 客户端
clapCLI 参数解析命令行工具标配
anyhow / thiserror错误处理简化 Result 使用
log + env_logger日志标准化日志方案
sqlx数据库编译期 SQL 检查的 ORM
rayon并行迭代一行代码变并行

一句话小结

模块系统(mod / use / pub)管理代码组织,Cargo 管理依赖和构建。Rust 的包管理体验是编译语言中最好的——一个 Cargo.toml 搞定一切,没有 CMake 的折磨。下一篇讲 测试

练习

  1. 创建一个 library crate,包含模块 math::operations,其中有一个公开函数 add(a: i32, b: i32) -> i32。在 lib.rs 中通过 pub use 重新导出。
  2. 修改 Cargo.toml,添加 rand 作为依赖,然后用 cargo build 验证。
参考答案
  1. 文件结构:
src/
  lib.rs
  math/
    mod.rs (或 math.rs + math/operations.rs)
    operations.rs

src/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;
  1. Cargo.toml 添加:
[dependencies]
rand = "0.8"
最后更新于