Rust: Rust 模块系统与 Cargo:代码组织与项目管理
最后更新:2026-08-26
模块系统是 Rust 的"代码寻址方案"——就像一栋楼的门牌号,让你能精确找到每一段代码的位置,并控制哪些代码可以被外部访问。
如果说一个大型项目是一座城市,那模块(mod)就是街区、文件就是建筑、函数就是房间。Cargo 则是城市管理局——负责修路、维护、检查质量。没有模块系统,代码就是堆在一起的"城中村"。
1. 你将学到
mod关键字定义模块与嵌套模块pub可见性控制——父/子/同级模块的访问规则use路径引入与super/crate相对路径Cargo.toml依赖管理与语义化版本号(semver)cargo build/test/bench/doc常用命令- workspace 多包管理与
lib.rs/main.rs分工
2. 公寓号牌系统的故事
(1) 痛苦:没有门牌号的城市
Tom 搬进了一栋新建的公寓楼,发现这里没有门牌号系统。
- 他想找 3 单元 502 室,但每栋楼看起来都一样
- 快递员送外卖要打电话问"你在哪栋楼?"
- 物业通知:"请 3 单元 5 楼的人下来拿快递"——结果 3 单元 5 楼有 6 户人家
- 更糟的是,新搬来的住户把自己家的门牌号改成了"阿里巴巴"——整个寻址系统崩溃了
"如果有个统一的门牌号规则就好了:楼栋-单元-房间……"
(2) Rust 模块系统的方案
公寓(Crate) → 一栋楼
楼栋名(crate name) → 楼号
单元(Module) → 单元门
房间号(Function) → 具体的房间
// 文件结构对应关系:
// src/
// main.rs → 公寓大堂(入口)
// building/
// mod.rs → 楼栋信息
// unit_1/
// mod.rs → 1 单元
// room_501.rs → 501 室
// room_502.rs → 502 室
// 在 Rust 中,通过 mod 和 pub 精确控制谁可以访问什么
mod building {
pub mod unit_1 {
pub fn room_501() -> &'static str {
"501 室:Tom 的家"
}
fn room_502() -> &'static str {
"502 室:私密空间" // 默认私有,外部不可见
}
}
}
fn main() {
// 通过完整路径访问
println!("{}", building::unit_1::room_501());
// println!("{}", building::unit_1::room_502()); // ❌ 私有函数,编译错误
}
Rust 的模块系统就像公寓的门牌号系统:
crate是整栋楼,mod是单元,fn是房间。pub控制哪些房间对外开门,没有pub的房间就是私密的——外人不能随便进。
3. 核心概念
(1) 模块系统与路径
graph TB
A[Rust 模块系统] --> B[mod 定义]
A --> C[pub 可见性]
A --> D[use 路径引入]
A --> E[Cargo 项目管理]
B --> B1["mod 模块名 { ... }"]
B --> B2["mod 模块名; // 从文件加载"]
C --> C1["pub: 对外可见"]
C --> C2["pub(crate): 仅 crate 内可见"]
C --> C3["pub(super): 仅父模块可见"]
C --> C4["无 pub: 私有的"]
D --> D1["use crate::a::b::c;"]
D --> D2["use super::module;"]
D --> D3["use self::module;"]
E --> E1["Cargo.toml 依赖"]
E --> E2["cargo build / test"]
E --> E3["workspace 多包"]
E --> E4["lib.rs vs main.rs"]
(2) 可见性规则对比
| 可见性 | 关键字 | 谁可以访问 | 类比 |
|---|---|---|---|
| 私有 | 无(默认) | 当前模块及子模块 | 只有自己家人能进卧室 |
| 父模块可见 | pub(super) |
父模块 | 楼上楼下邻居可以串门 |
| Crate 内可见 | pub(crate) |
当前 crate 内所有模块 | 小区业主可以进小区大门 |
| 公开 | pub |
所有外部 crate | 任何人都可以进商场 |
(3) 路径类型
| 路径类型 | 前缀 | 示例 | 说明 |
|---|---|---|---|
| 绝对路径 | crate:: |
crate::utils::helper::foo |
从 crate 根开始 |
| 相对路径 | self:: |
self::helper::foo |
从当前模块开始 |
| 相对路径 | super:: |
super::helper::foo |
从父模块开始 |
| 外部路径 | 包名 | serde::Serialize |
从外部 crate 开始 |
(4) Cargo 常用命令速查
| 命令 | 作用 | 常用选项 |
|---|---|---|
cargo new |
创建新项目 | --lib(库项目) |
cargo build |
编译项目 | --release(优化编译) |
cargo run |
编译并运行 | --bin name(指定二进制) |
cargo check |
快速检查编译错误 | 比 build 快,不生成二进制 |
cargo test |
运行测试 | test_name(指定测试) |
cargo doc |
生成文档 | --open(自动打开浏览器) |
cargo clippy |
代码 lint 检查 | -W clippy::all |
cargo fmt |
代码格式化 | --check(仅检查不修改) |
cargo add |
添加依赖 | --features xxx |
cargo update |
更新依赖锁文件 | 更新 Cargo.lock |
cargo publish |
发布到 crates.io | 需先登录 |
cargo clean |
清理编译产物 | 删除 target/ 目录 |
4. 模块与 Cargo 示例
▶ 示例 1:模块定义与 pub 可见性(难度 ⭐)
// ============================================
// 模块嵌套、pub 可见性、路径访问
// 演示:餐厅的厨房对顾客不可见,但对服务员可见
// ============================================
// 定义餐厅模块
mod restaurant {
// 公开的:顾客可以进入餐厅
pub struct Menu {
pub name: String,
price: f64, // 默认私有,外部不可见
}
impl Menu {
// 公开的构造函数
pub fn new(name: &str, price: f64) -> Menu {
Menu {
name: name.to_string(),
price,
}
}
// 公开的方法:获取价格
pub fn get_price(&self) -> f64 {
self.price
}
}
// 公开的:顾客可以点餐
pub fn order_food(item: &str) -> String {
// 私有的:厨房的操作对顾客不可见
let prepared = prepare_in_kitchen(item);
format!("您的 {} 已准备好: {}", item, prepared)
}
// 私有的:厨房是顾客不能进入的
fn prepare_in_kitchen(item: &str) -> String {
format!("[后厨] {} 正在烹饪中...", item)
}
// 嵌套模块:厨房内部
mod kitchen {
// 私有的存储区
pub struct Storage {
pub items: Vec<String>,
}
impl Storage {
pub fn new() -> Storage {
Storage {
items: vec![
"蔬菜".to_string(),
"肉类".to_string(),
"调料".to_string(),
],
}
}
}
}
}
fn main() {
// 访问公开的模块成员
let dish = restaurant::order_food("宫保鸡丁");
println!("{}", dish);
// 创建公开的结构体
let menu_item = restaurant::Menu::new("宫保鸡丁", 38.0);
println!("菜品: {}, 价格: {:.1}元", menu_item.name, menu_item.get_price());
// 以下代码无法编译(取消注释可试):
// println!("厨师信息: {}", restaurant::prepare_in_kitchen("宫保鸡丁")); // ❌ 私有函数
// println!("价格: {}", menu_item.price); // ❌ 私有字段
// let storage = restaurant::kitchen::Storage::new(); // ❌ kitchen 模块是私有的
}
输出:
您的 宫保鸡丁 已准备好: [后厨] 宫保鸡丁 正在烹饪中...
菜品: 宫保鸡丁, 价格: 38.0元
模块的可见性规则就像餐厅的物理布局:顾客(外部代码)只能进入餐厅大厅(
pub模块),不能进入厨房(私有模块)。厨房里的操作(prepare_in_kitchen)对外完全不可见——这就是封装(encapsulation)。
▶ 示例 2:use 路径与 super/crate(难度 ⭐⭐)
// ============================================
// use 关键字引入路径、super 与 crate 相对路径
// 模拟:公司组织架构——部门→小组→员工
// ============================================
// 顶层模块:公司
mod company {
// 工程部门
pub mod engineering {
pub fn team_name() -> &'static str {
"工程部"
}
// 前端小组
pub mod frontend {
pub fn member_count() -> u32 {
5
}
// 使用 super 访问父模块(engineering)
pub fn full_info() -> String {
format!("{} 前端小组,{}人", super::team_name(), member_count())
}
}
// 后端小组
pub mod backend {
pub fn member_count() -> u32 {
8
}
// 使用 super 访问父模块
pub fn full_info() -> String {
format!("{} 后端小组,{}人", super::team_name(), member_count())
}
}
}
// 市场部门
pub mod marketing {
pub fn team_name() -> &'static str {
"市场部"
}
// 使用 crate 路径从根访问
pub fn total_employees() -> u32 {
// 从 crate 根开始访问
crate::company::engineering::frontend::member_count()
+ crate::company::engineering::backend::member_count()
+ self::member_count()
}
fn member_count() -> u32 {
6
}
}
}
// 使用 use 引入路径——简化调用
use company::engineering::frontend;
use company::engineering::backend;
use company::marketing;
fn main() {
// 方式 1:完整路径(不推荐,写起来太长)
println!("{}", company::engineering::frontend::full_info());
// 方式 2:use 引入后直接调用(推荐)
println!("{}", frontend::full_info());
println!("{}", backend::full_info());
// 引入 marketing 模块
println!("市场部人数: {}", marketing::member_count());
// 使用 crate 路径访问
println!("公司总人数: {}", marketing::total_employees());
// 使用 as 别名避免冲突
use company::engineering as eng;
println!("部门: {}", eng::team_name());
}
输出:
工程部 前端小组,5人
工程部 后端小组,8人
市场部人数: 6
公司总人数: 19
部门: 工程部
use就像给"门牌号"设置快捷方式——不用每次都写完整的company::engineering::frontend::full_info()。super是"往上走一层"(父模块),crate是"回到大楼入口"(crate 根)。as关键字可以给路径取别名,解决同名冲突。
▶ 示例 3:Cargo.toml 依赖管理与项目结构(难度 ⭐⭐)
// ============================================
// 模拟 Cargo 项目结构 + 依赖管理
// 演示:lib.rs 与 main.rs 的分工、外部依赖使用
// ============================================
// 注意:这个示例演示的是 lib.rs 中的代码
// 实际的 Cargo.toml 文件内容见下方注释
// ============================================
// Cargo.toml 内容(模拟):
// ============================================
// [package]
// name = "my-toolkit"
// version = "0.1.0"
// edition = "2021"
//
// [dependencies]
// serde = { version = "1.0", features = ["derive"] }
// serde_json = "1.0"
// chrono = "0.4"
// regex = "1.10"
//
// [dev-dependencies]
// rand = "0.8"
//
// [profile.release]
// opt-level = 3
// ============================================
// 工具模块:日期处理
pub mod date_utils {
pub fn format_today() -> String {
// 实际项目中会使用 chrono::Local::now()
"2026-07-03".to_string()
}
pub fn is_weekend(day: &str) -> bool {
day.ends_with("周六") || day.ends_with("周日")
}
}
// 工具模块:字符串处理
pub mod string_utils {
/// 验证邮箱格式(模拟正则匹配)
pub fn validate_email(email: &str) -> bool {
// 简化版验证:实际用 regex crate
email.contains('@') && email.contains('.')
}
/// 去除非字母数字字符(模拟)
pub fn sanitize(input: &str) -> String {
input.chars()
.filter(|c| c.is_alphanumeric() || *c == ' ')
.collect()
}
}
// 工具模块:数学计算
pub mod math_utils {
/// 计算斐波那契数列第 n 项
pub fn fibonacci(n: u32) -> u64 {
match n {
0 => 0,
1 => 1,
_ => fibonacci(n - 1) + fibonacci(n - 2),
}
}
/// 判断是否为质数
pub fn is_prime(n: u32) -> bool {
if n < 2 {
return false;
}
let limit = (n as f64).sqrt() as u32;
for i in 2..=limit {
if n % i == 0 {
return false;
}
}
true
}
}
// 测试模块(使用 #[cfg(test)] 条件编译)
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_validate_email() {
assert!(string_utils::validate_email("user@example.com"));
assert!(!string_utils::validate_email("invalid"));
}
#[test]
fn test_fibonacci() {
assert_eq!(math_utils::fibonacci(0), 0);
assert_eq!(math_utils::fibonacci(1), 1);
assert_eq!(math_utils::fibonacci(10), 55);
}
#[test]
fn test_is_prime() {
assert!(math_utils::is_prime(17));
assert!(!math_utils::is_prime(1));
assert!(!math_utils::is_prime(4));
}
#[test]
fn test_sanitize() {
assert_eq!(string_utils::sanitize("hello@world!"), "hello world");
}
}
// ============================================
// main.rs 中的代码(模拟):
// ============================================
// use my_toolkit::{
// date_utils,
// string_utils,
// math_utils,
// };
//
// fn main() {
// println!("今天日期: {}", date_utils::format_today());
// println!("邮箱验证: {}", string_utils::validate_email("test@example.com"));
// println!("质数检查: {}", math_utils::is_prime(17));
// }
fn main() {
// 演示各工具函数
println!("=== 工具库演示 ===");
// 日期工具
println!("今天: {}", date_utils::format_today());
println!("是否是周末: {}", date_utils::is_weekend("周六"));
// 字符串工具
println!("邮箱验证 test@example.com: {}", string_utils::validate_email("test@example.com"));
println!("邮箱验证 invalid: {}", string_utils::validate_email("invalid"));
println!("净化 'hello@world!': {}", string_utils::sanitize("hello@world!"));
// 数学工具
println!("斐波那契第10项: {}", math_utils::fibonacci(10));
println!("17 是质数吗: {}", math_utils::is_prime(17));
println!("4 是质数吗: {}", math_utils::is_prime(4));
println!("=== 演示结束 ===");
// 运行测试的说明(实际项目中使用 cargo test)
println!("使用 `cargo test` 运行单元测试");
}
输出:
=== 工具库演示 ===
今天: 2026-07-03
是否是周末: true
邮箱验证 test@example.com: true
邮箱验证 invalid: false
净化 'hello@world!': hello world
斐波那契第10项: 55
17 是质数吗: true
4 是质数吗: false
=== 演示结束 ===
使用 `cargo test` 运行单元测试
真实项目的标准结构:
lib.rs放库代码(公共 API),main.rs放可执行入口(使用 lib)。Cargo.toml管理依赖,[dependencies]是生产依赖,[dev-dependencies]是测试/构建工具依赖。cargo test自动发现并运行#[test]标记的函数。
▶ 示例 4:Cargo 常用命令与 workspace(难度 ⭐⭐⭐)
// ============================================
// Cargo 常用命令与 workspace 多包管理
// 模拟:一个 "任务管理器" workspace 项目
// ============================================
// ============================================
// 顶层 Cargo.toml(workspace):
// ============================================
// [workspace]
// members = [
// "task-core", // 核心库
// "task-cli", // CLI 工具
// "task-web", // Web 接口
// ]
//
// [workspace.package]
// version = "1.0.0"
// edition = "2021"
// ============================================
// ============================================
// task-core/Cargo.toml:
// ============================================
// [package]
// name = "task-core"
// version.workspace = true
// edition.workspace = true
//
// [dependencies]
// serde = { version = "1.0", features = ["derive"] }
// chrono = "0.4"
// ============================================
// 模拟 task-core 库的代码
pub mod task_core {
use std::collections::HashMap;
/// 任务优先级
#[derive(Debug, Clone, PartialEq)]
pub enum Priority {
Low,
Medium,
High,
Urgent,
}
/// 任务状态
#[derive(Debug, Clone, PartialEq)]
pub enum Status {
Todo,
InProgress,
Done,
Cancelled,
}
/// 核心任务结构体
#[derive(Debug, Clone)]
pub struct Task {
pub id: u64,
pub title: String,
pub priority: Priority,
pub status: Status,
pub tags: Vec<String>,
}
impl Task {
pub fn new(id: u64, title: &str, priority: Priority) -> Task {
Task {
id,
title: title.to_string(),
priority,
status: Status::Todo,
tags: Vec::new(),
}
}
pub fn add_tag(&mut self, tag: &str) {
self.tags.push(tag.to_string());
}
pub fn is_completed(&self) -> bool {
self.status == Status::Done || self.status == Status::Cancelled
}
}
/// 任务管理器
pub struct TaskManager {
tasks: HashMap<u64, Task>,
next_id: u64,
}
impl TaskManager {
pub fn new() -> TaskManager {
TaskManager {
tasks: HashMap::new(),
next_id: 1,
}
}
pub fn create_task(&mut self, title: &str, priority: Priority) -> u64 {
let id = self.next_id;
self.next_id += 1;
let task = Task::new(id, title, priority);
self.tasks.insert(id, task);
id
}
pub fn get_task(&self, id: u64) -> Option<&Task> {
self.tasks.get(&id)
}
pub fn complete_task(&mut self, id: u64) -> bool {
if let Some(task) = self.tasks.get_mut(&id) {
task.status = Status::Done;
true
} else {
false
}
}
pub fn list_tasks(&self) -> Vec<&Task> {
let mut tasks: Vec<&Task> = self.tasks.values().collect();
tasks.sort_by_key(|t| t.id);
tasks
}
}
}
// ============================================
// Cargo 命令参考(注释中演示):
// ============================================
// 常用命令:
// cargo new project_name -- 创建新项目
// cargo build -- 编译(debug)
// cargo build --release -- 编译(release 优化)
// cargo run -- 编译 + 运行
// cargo check -- 快速检查编译错误(不生成二进制)
// cargo test -- 运行测试
// cargo test test_name -- 运行指定测试
// cargo bench -- 运行性能基准测试
// cargo doc --open -- 生成文档并打开
// cargo clippy -- 代码 lint 检查
// cargo fmt -- 代码格式化
// cargo add crate_name -- 添加依赖
// cargo update -- 更新依赖
// cargo publish -- 发布到 crates.io
// cargo clean -- 清理编译产物
//
// Workspace 命令:
// cargo build --workspace -- 编译 workspace 中所有包
// cargo test -p task-core -- 只测试指定包
// cargo run -p task-cli -- 运行指定包
fn main() {
use task_core::{Priority, TaskManager};
println!("=== 任务管理器 (模拟 Workspace 项目) ===");
let mut manager = TaskManager::new();
// 创建任务
let id1 = manager.create_task("学习 Rust 智能指针", Priority::High);
let id2 = manager.create_task("完成模块系统练习", Priority::Medium);
let id3 = manager.create_task("修复生产环境 Bug", Priority::Urgent);
// 列出所有任务
println!("\n--- 所有任务 ---");
for task in manager.list_tasks() {
println!("#{} [{:?}] {} - {:?}", task.id, task.priority, task.title, task.status);
}
// 完成一个任务
manager.complete_task(id1);
println!("\n完成 #{} 后:", id1);
for task in manager.list_tasks() {
let status = if task.is_completed() { "已完成" } else { "进行中" };
println!("#{} {} - {}", task.id, task.title, status);
}
// 获取单个任务
if let Some(task) = manager.get_task(id3) {
println!("\n紧急任务: #{} {} ({:?})", task.id, task.title, task.priority);
}
println!("\n=== 演示结束 ===");
println!("项目结构: task-core (库) + task-cli (CLI) + task-web (Web)");
println!("使用 `cargo test -p task-core` 测试核心库");
println!("使用 `cargo doc --open` 生成文档");
}
输出:
=== 任务管理器 (模拟 Workspace 项目) ===
--- 所有任务 ---
#1 [High] 学习 Rust 智能指针 - Todo
#2 [Medium] 完成模块系统练习 - Todo
#3 [Urgent] 修复生产环境 Bug - Todo
完成 #1 后:
#1 学习 Rust 智能指针 - 已完成
#2 完成模块系统练习 - 进行中
#3 修复生产环境 Bug - 进行中
紧急任务: #3 修复生产环境 Bug (Urgent)
=== 演示结束 ===
项目结构: task-core (库) + task-cli (CLI) + task-web (Web)
使用 `cargo test -p task-core` 测试核心库
使用 `cargo doc --open` 生成文档
Workspace 是管理多包项目的利器:
task-core提供核心类型和逻辑(库),task-cli提供命令行界面(可执行文件),task-web提供 Web API(另一个可执行文件)。cargo build --workspace一次性编译所有包。cargo test -p task-core只测试核心库。
▶ 示例 5:综合练习——模块化设计模拟(难度 ⭐⭐⭐)
// ============================================
// 综合示例:模块可见性与 API 设计
// 模拟多文件项目结构(实际项目应拆分为独立文件)
// ============================================
mod math_utils {
pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn multiply(a: i32, b: i32) -> i32 { a * b }
fn internal_check(val: i32) -> bool { val >= 0 }
pub fn safe_divide(a: i32, b: i32) -> Option<i32> {
if b == 0 { return None; }
if !internal_check(a) || !internal_check(b) { return None; }
Some(a / b)
}
pub mod constants {
pub const PI: f64 = 3.14159265358979;
pub const E: f64 = 2.71828182845905;
pub const MAX_I32: i32 = i32::MAX;
}
}
mod string_utils {
pub fn capitalize(s: &str) -> String {
let mut chars = s.chars();
match chars.next() {
None => String::new(),
Some(first) => first.to_uppercase().collect::<String>() + chars.as_str(),
}
}
pub fn truncate(s: &str, max_len: usize) -> String {
if s.len() <= max_len { s.to_string() }
else { format!("{}...", &s[..max_len.min(s.len())]) }
}
}
mod user {
pub struct User {
pub name: String,
age: u8,
email: String,
}
impl User {
pub fn new(name: &str, age: u8, email: &str) -> Self {
User { name: name.to_string(), age, email: email.to_string() }
}
pub fn age(&self) -> u8 { self.age }
pub fn summary(&self) -> String {
format!("{} ({}岁, {})", self.name, self.age, self.email)
}
}
}
fn main() {
use math_utils::{add, multiply, safe_divide, constants};
use string_utils::{capitalize, truncate};
use user::User;
println!("=== math_utils 模块 ===");
println!("2 + 3 = {}", add(2, 3));
println!("4 * 5 = {}", multiply(4, 5));
println!("10 / 3 = {:?}", safe_divide(10, 3));
println!("10 / 0 = {:?}", safe_divide(10, 0));
println!("PI = {:.5}, E = {:.5}", constants::PI, constants::E);
println!("\n=== string_utils 模块 ===");
println!("capitalize: '{}'", capitalize("rust"));
println!("truncate: '{}'", truncate("Hello, World!", 8));
println!("\n=== user 模块 ===");
let alice = User::new("Alice", 30, "alice@example.com");
println!("{}", alice.summary());
println!("年龄: {}", alice.age());
}
输出:
=== math_utils 模块 ===
2 + 3 = 5
4 * 5 = 20
10 / 3 = Some(3)
10 / 0 = None
PI = 3.14159, E = 2.71828
=== string_utils 模块 ===
capitalize: 'Rust'
truncate: 'Hello, ...'
=== user 模块 ===
Alice (30岁, alice@example.com)
年龄: 30
模块化设计三原则:
pub只暴露必要的 API(如add、safe_divide),内部细节(如internal_check)保持私有;子模块(如constants)用pub mod暴露;结构体字段逐个标注可见性(pub name/age私有 +age()getter)。
❓ 常见问题
mod 和 fn 有什么区别?为什么不用文件来组织?mod 是模块定义,fn 是函数定义。pub(crate) 和 pub 有什么区别?pub(crate) 只对同一 crate 内的代码可见,pub 对所有外部 crate 可见。use super::xxx 和 use crate::xxx 什么时候用?super 用于访问父模块(相对路径),crate 用于从 crate 根开始访问(绝对路径)。^1.2.3 是什么意思?^ 是"兼容性更新"——允许 >=1.2.3 且 <2.0.0 的版本。lib.rs 和 main.rs 可以同时存在吗?📖 小节
mod关键字定义模块,可以嵌套(mod outer { mod inner { ... } })或从文件加载(mod xxx;)pub控制可见性:默认私有,pub公开,pub(crate)仅 crate 内可见,pub(super)仅父模块可见use引入路径简化调用,支持super(父模块)和crate(根模块)两种相对/绝对路径Cargo.toml管理依赖,使用语义化版本号(semver),[dependencies]与[dev-dependencies]分离cargo build/test/doc/bench是 Cargo 的核心命令,cargo clippy和cargo fmt保证代码质量- Workspace 多包管理通过顶层
Cargo.toml的[workspace]配置,members列出所有子包
📝 作业
- 难度 ⭐:创建一个包含两个模块
math和greeting的程序。math模块有一个公开函数add(a: i32, b: i32) -> i32,greeting模块有一个公开函数say_hello(name: &str) -> String。在main中调用这两个函数。 - 难度 ⭐⭐:模拟一个"图书馆"模块系统。创建
library模块,包含books子模块(图书管理)和members子模块(会员管理)。books有add_book和list_books函数,members有add_member和list_members函数。使用pub(super)和pub(crate)合理控制可见性。在main中演示添加图书和会员。 - 难度 ⭐⭐⭐:研究 Cargo workspace 项目结构。在本地创建一个 workspace 项目,包含
core-lib(库,提供add、subtract函数)和cli-app(可执行文件,通过core-lib做计算并打印结果)。配置Cargo.toml的 workspace 设置,使用cargo build --workspace编译,cargo run -p cli-app运行。