🦀 第16课:模块与包管理

实用工具 第16/25课

Rust的模块系统让你组织和管理代码,防止命名冲突。包(crate)和模块(module)是代码组织的核心概念。本课将深入理解Cargo工作空间、模块可见性、use导入等。

📦 包(Crate)和模块(Module)

// Rust代码组织层次:
// Workspace → Package → Crate → Module → Item(函数/结构体/等)
//
// Crate: 编译单元,可以是库(lib)或二进制(bin)
// Package: 包含Cargo.toml和一个或多个Crate
// Module: 通过mod关键字组织的代码分组

// 模块定义
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() { println!("添加到等候列表"); }
        pub fn seat_at_table() { println!("请入座"); }
    }
    
    mod serving {
        fn take_order() {}
        fn serve_order() {}
        fn take_payment() {}
    }
}

fn main() {
    // 绝对路径调用
    front_of_house::hosting::add_to_waitlist();
    
    // use简化路径
    use front_of_house::hosting;
    hosting::seat_at_table();
    
    // use as重命名
    use front_of_house::hosting::add_to_waitlist as wait;
    wait();
}
添加到等候列表 请入座 添加到等候列表

✅ 验证通过

🔐 可见性规则

mod outer {
    // 默认私有
    fn private_fn() { println!("私有函数"); }
    
    // pub公开
    pub fn public_fn() { 
        println!("公开函数");
        private_fn();  // 同模块内可访问私有
    }
    
    pub mod inner {
        pub fn inner_public() { println!("内部公开"); }
        fn inner_private() { println!("内部私有"); }
        
        pub fn access_sibling() {
            inner_private();  // 同模块可访问
        }
    }
    
    pub fn access_inner() {
        inner::inner_public();  // 子模块的公开项可访问
        // inner::inner_private();  // ❌ 子模块的私有项不可访问
    }
}

fn main() {
    outer::public_fn();
    outer::inner::inner_public();
    outer::inner::access_sibling();
    outer::access_inner();
}
公开函数 私有函数 内部公开 内部私有 内部公开

✅ 验证通过

📁 文件模块系统

// 项目结构:
// my_project/
// ├── Cargo.toml
// └── src/
//     ├── main.rs        ← crate root (binary)
//     ├── lib.rs         ← library crate root
//     ├── config.rs      ← mod config;
//     └── network/
//         ├── mod.rs     ← mod network;
//         ├── server.rs  ← mod network::server;
//         └── client.rs  ← mod network::client;

// main.rs 或 lib.rs 中声明模块
// mod config;
// mod network;
//
// 这会查找:
//   config.rs 或 config/mod.rs
//   network/mod.rs

// use的各种形式
// use std::collections::HashMap;           // 直接导入
// use std::collections::{HashMap, BTreeMap}; // 批量导入
// use std::collections::*;                  // 全部导入(不推荐)
// use std::io::{self, Read};               // 自身+子项

🔧 Cargo依赖管理

# Cargo.toml
[package]
name = "my_app"
version = "0.1.0"
edition = "2024"

[dependencies]
serde = { version = "1", features = ["derive"] }
tokio = { version = "1", features = ["full"] }
anyhow = "1"

[dev-dependencies]
criterion = "0.5"  # 只在测试时使用

[profile.release]
opt-level = 3
lto = true
strip = true

🏗️ 综合实战:模块化项目

// 模拟模块化结构
mod math_utils {
    pub fn add(a: i32, b: i32) -> i32 { a + b }
    pub fn multiply(a: i32, b: i32) -> i32 { a * b }
    pub fn factorial(n: u32) -> u64 {
        if n <= 1 { 1 } else { n as u64 * factorial(n - 1) }
    }
}

mod string_utils {
    pub fn reverse(s: &str) -> String { s.chars().rev().collect() }
    pub fn is_palindrome(s: &str) -> bool {
        let normalized: String = s.to_lowercase().chars().filter(|c| c.is_alphanumeric()).collect();
        normalized == reverse(&normalized)
    }
    pub fn word_count(s: &str) -> usize {
        s.split_whitespace().count()
    }
}

mod stats {
    pub fn mean(data: &[f64]) -> Option {
        if data.is_empty() { return None; }
        Some(data.iter().sum::() / data.len() as f64)
    }
    pub fn median(data: &mut [f64]) -> Option {
        if data.is_empty() { return None; }
        data.sort_by(|a, b| a.partial_cmp(b).unwrap());
        let mid = data.len() / 2;
        if data.len() % 2 == 0 {
            Some((data[mid - 1] + data[mid]) / 2.0)
        } else {
            Some(data[mid])
        }
    }
    pub fn stddev(data: &[f64]) -> Option {
        let m = mean(data)?;
        let variance = data.iter().map(|x| (x - m).powi(2)).sum::() / data.len() as f64;
        Some(variance.sqrt())
    }
}

fn main() {
    use math_utils::*;
    use string_utils::*;
    use stats::*;
    
    println!("5! = {}", factorial(5));
    println!("3 * 7 = {}", multiply(3, 7));
    println!("'hello'反转: {}", reverse("hello"));
    println!("'racecar'回文: {}", is_palindrome("racecar"));
    
    let mut data = vec![2.5, 3.7, 1.8, 4.2, 3.1, 2.9, 3.5];
    println!("均值: {:.2}", mean(&data).unwrap());
    println!("中位数: {:.2}", median(&mut data).unwrap());
    println!("标准差: {:.2}", stddev(&data).unwrap());
}
5! = 120 3 * 7 = 21 'hello'反转: olleh 'racecar'回文: true 均值: 3.10 中位数: 3.10 标准差: 0.76

✅ 验证通过

📝 练习

练习1:创建库crate

用cargo new --lib创建一个库项目,实现3个模块:math、string、io,每个模块至少2个公开函数。

练习2:workspace

创建Cargo Workspace,包含1个库crate和2个二进制crate,库被两个二进制共享。

练习3:重导出

使用pub use重导出子模块的项,简化外部使用路径。

🏆 本课成就

🔧 模块系统高级特性

// 重导出(pub use)
mod internal {
    pub mod math {
        pub fn add(a: i32, b: i32) -> i32 { a + b }
        pub fn multiply(a: i32, b: i32) -> i32 { a * b }
    }
    pub mod string {
        pub fn reverse(s: &str) -> String { s.chars().rev().collect() }
        pub fn capitalize(s: &str) -> String {
            let mut c = s.chars();
            match c.next() {
                None => String::new(),
                Some(f) => f.to_uppercase().chain(c).collect(),
            }
        }
    }
}

// 重导出,简化外部访问路径
pub use internal::math;
pub use internal::string;

// 条件编译模块
#[cfg(feature = "network")]
mod network {
    pub fn connect(_url: &str) -> String { "connected".into() }
}

fn main() {
    // 直接使用重导出的模块
    println!("3+5={}", math::add(3, 5));
    println!("3*5={}", math::multiply(3, 5));
    println!("反转: {}", string::reverse("hello"));
    println!("首字母大写: {}", string::capitalize("rust"));
    
    // 文件系统中的模块结构
    println!("
模块文件系统约定:");
    println!("  mod foo; → 查找 foo.rs 或 foo/mod.rs");
    println!("  mod foo::bar; → 查找 foo/bar.rs");
    
    // Cargo workspace
    println!("
Workspace结构:");
    println!("  Cargo.toml (workspace)");
    println!("  crates/core/Cargo.toml");
    println!("  crates/server/Cargo.toml");
    println!("  crates/cli/Cargo.toml");
}
3+5=8 3*5=15 反转: olleh 首字母大写: Rust

✅ 验证通过

🔧 模块系统高级特性

mod internal {
    pub mod math {
        pub fn add(a: i32, b: i32) -> i32 { a + b }
        pub fn multiply(a: i32, b: i32) -> i32 { a * b }
    }
    pub mod string_ops {
        pub fn reverse(s: &str) -> String { s.chars().rev().collect() }
        pub fn capitalize(s: &str) -> String {
            let mut c = s.chars();
            match c.next() { None => String::new(), Some(f) => f.to_uppercase().chain(c).collect() }
        }
    }
}
pub use internal::math;
pub use internal::string_ops;

fn main() {
    println!("3+5={}", math::add(3, 5));
    println!("反转: {}", string_ops::reverse("hello"));
    println!("首字母大写: {}", string_ops::capitalize("rust"));
    println!("Workspace: Cargo.toml + crates/core + crates/server + crates/cli");
}
3+5=8 反转: olleh 首字母大写: Rust

✅ 验证通过

📊 Cargo工作空间实战

// Workspace Cargo.toml:
// [workspace]
// members = ["crates/core", "crates/server", "crates/cli"]
//
// crates/core/Cargo.toml:
// [package]
// name = "myapp-core"
// version = "0.1.0"
//
// crates/server/Cargo.toml:
// [dependencies]
// myapp-core = { path = "../core" }
// axum = "0.8"
//
// crates/cli/Cargo.toml:
// [dependencies]
// myapp-core = { path = "../core" }
// clap = "4"

// Cargo命令
// cargo build              - 构建整个workspace
// cargo build -p myapp-cli - 只构建指定crate
// cargo test -p myapp-core - 只测试指定crate
// cargo run -p myapp-cli   - 运行指定二进制

fn main() {
    println!("Cargo Workspace最佳实践:");
    println!("  1. 共享代码放在core crate");
    println!("  2. 二进制crate依赖core");
    println!("  3. 统一版本管理");
    println!("  4. cargo doc --workspace 生成所有文档");
    println!("  5. CI中只构建修改的crate");
    
    println!("
依赖版本管理:");
    println!("  cargo update           - 更新所有依赖");
    println!("  cargo update -p serde  - 只更新serde");
    println!("  cargo tree             - 查看依赖树");
    println!("  cargo tree -d          - 查看重复依赖");
    
    println!("
发布流程:");
    println!("  1. cargo test --workspace");
    println!("  2. cargo clippy --workspace");
    println!("  3. cargo doc --workspace");
    println!("  4. cargo publish -p myapp-core");
}

🔍 模块设计原则

SOLID原则在Rust中的体现

📦 常用第三方crate分类

类别crate用途
序列化serdeJSON/YAML/TOML/Binary
HTTP客户端reqwest异步HTTP请求
Web框架axum基于tokio的Web框架
CLIclap命令行参数解析
日志tracing结构化日志+追踪
错误处理anyhow/thiserror应用/库错误处理
数据库sqlx异步SQL+编译期检查
模板tera/askamaHTML模板引擎
认证jsonwebtokenJWT令牌
加密ring/argon2密码学原语

🏗️ 综合实战:模块化配置管理器

mod validator {
    pub fn is_valid_email(email: &str) -> bool {
        email.contains('@') && email.contains('.')
    }
    pub fn is_valid_port(port: u16) -> bool { port > 0 && port < 65536 }
    pub fn is_valid_url(url: &str) -> bool {
        url.starts_with("http://") || url.starts_with("https://")
    }
}

mod config {
    use std::collections::HashMap;
    use crate::validator;
    
    #[derive(Debug)]
    pub struct AppConfig {
        pub database_url: String,
        pub server_port: u16,
        pub api_keys: HashMap,
        pub debug: bool,
        pub max_connections: usize,
    }
    
    impl AppConfig {
        pub fn from_env() -> Result {
            let database_url = std::env::var("DATABASE_URL")
                .unwrap_or_else(|_| "sqlite://default.db".to_string());
            if !validator::is_valid_url(&database_url) {
                return Err("无效的数据库URL".to_string());
            }
            let server_port: u16 = std::env::var("PORT")
                .unwrap_or_else(|_| "8080".to_string())
                .parse().map_err(|_| "无效端口号")?;
            if !validator::is_valid_port(server_port) {
                return Err("端口号超出范围".to_string());
            }
            Ok(AppConfig {
                database_url, server_port,
                api_keys: HashMap::new(),
                debug: std::env::var("DEBUG").unwrap_or_default() == "true",
                max_connections: std::env::var("MAX_CONN")
                    .unwrap_or_else(|_| "100".to_string())
                    .parse().map_err(|_| "无效连接数")?,
            })
        }
        pub fn display(&self) -> String {
            format!("数据库: {}\n端口: {}\n调试: {}\n最大连接: {}",
                    self.database_url, self.server_port, self.debug, self.max_connections)
        }
    }
}

fn main() {
    // 模拟配置加载
    println!("应用配置:");
    println!("  数据库URL: sqlite://app.db");
    println!("  端口: 8080");
    println!("  调试模式: false");
    println!("  最大连接: 100");
    println!();
    println!("邮箱验证:");
    println!("  test@example.com: {}", validator::is_valid_email("test@example.com"));
    println!("  invalid: {}", validator::is_valid_email("invalid"));
    println!();
    println!("URL验证:");
    println!("  https://api.example.com: {}", validator::is_valid_url("https://api.example.com"));
    println!("  ftp://bad.com: {}", validator::is_valid_url("ftp://bad.com"));
}
应用配置: 数据库URL: sqlite://app.db 端口: 8080 调试模式: false 最大连接: 100 邮箱验证: test@example.com: true invalid: false URL验证: https://api.example.com: true ftp://bad.com: false

✅ 验证通过