模块系统、crate 与 workspace intermediate 25 分钟 更新 2026-06-15

库 crate 与 workspace

区分 lib 与 bin,用 workspace 管理多个 crate。

库 crate 与 workspace

你会学到什么

  • 一个 package 可以同时包含库(lib.rs)和二进制(main.rs)。
  • 库 crate 通过 pub API 对外提供可复用逻辑。
  • workspace 把多个相关 crate 放在一个仓库里统一管理。

最小示例

// src/lib.rs —— 库,对外暴露 API
pub fn celsius_to_fahrenheit(c: f64) -> f64 {
    c * 9.0 / 5.0 + 32.0
}
// src/main.rs —— 二进制,按 crate 名引用库
use rt_29_library::celsius_to_fahrenheit;

运行代码

cd examples
cargo run -p rt_29_library
cargo test -p rt_29_library

代码讲解

库 crate 的根是 lib.rs,里面 pub 的项就是公开 API;二进制 crate 的根是 main.rs。当二者在同一个 package 时,main.rs 用 crate 名引用库:

use rt_29_library::{celsius_to_fahrenheit, fahrenheit_to_celsius};

本教程的 examples/ 目录本身就是一个 workspace。根 Cargo.toml[workspace] 列出所有成员,它们共享同一个 Cargo.locktarget/:

[workspace]
members = ["00_hello_world", "01_cargo_project", "..."]

[workspace.package]
version = "0.1.0"
edition = "2024"

成员用 version.workspace = true 继承公共配置,避免重复。

常见错误

在 workspace 成员里各写各的依赖版本,导致版本漂移。可以用 [workspace.dependencies] 统一声明,成员再用 dep.workspace = true 继承。

练习

  • 29_library 的转换函数补一个 kelvin_to_celsius
  • 新建一个 crate 依赖 rt_29_library,在 Cargo.toml 里用 path 引用它。

小结

库 crate 提供可复用 API,二进制 crate 提供入口,workspace 让多个 crate 共享配置与构建产物。

下一步

工程化的下一步是测试与文档。下一章进入质量保障阶段。

完整示例代码

下面是 examples/29_library/ 的完整源码。无需 clone 仓库,直接在页面上阅读、复制、对照运行。

examples/29_library/src/main.rs
//! 二进制部分:通过 crate 名引用同包的库。

use rt_29_library::{celsius_to_fahrenheit, fahrenheit_to_celsius};

fn main() {
    println!("0°C = {}°F", celsius_to_fahrenheit(0.0));
    println!("212°F = {}°C", fahrenheit_to_celsius(212.0));
}
examples/29_library/src/lib.rs
//! 一个最小库 crate:温度单位转换。
//!
//! 库 crate 通过 `lib.rs` 对外暴露 API,二进制或其他 crate 都能复用。

/// 摄氏度转华氏度。
pub fn celsius_to_fahrenheit(celsius: f64) -> f64 {
    celsius * 9.0 / 5.0 + 32.0
}

/// 华氏度转摄氏度。
pub fn fahrenheit_to_celsius(fahrenheit: f64) -> f64 {
    (fahrenheit - 32.0) * 5.0 / 9.0
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn converts_to_fahrenheit() {
        assert_eq!(celsius_to_fahrenheit(0.0), 32.0);
        assert_eq!(celsius_to_fahrenheit(100.0), 212.0);
    }

    #[test]
    fn round_trips() {
        let back = fahrenheit_to_celsius(celsius_to_fahrenheit(37.0));
        assert!((back - 37.0).abs() < 1e-9);
    }
}
examples/29_library/Cargo.toml
[package]
name = "rt_29_library"
version.workspace = true
edition.workspace = true
publish.workspace = true