rustbook/04.md

27 KiB
Raw Permalink Blame History

第四章 结构化工程:模块与包

4.1 模块系统概述

随着项目规模增长代码组织变得至关重要。Rust的模块系统提供了强大的工具帮助开发者构建清晰、可维护的代码结构。本章将探索如何使用模块、包和工作空间来组织大型Rust项目。

为什么需要模块化?

模块化设计带来的核心优势:

  • 代码组织 - 逻辑相关的代码集中在一起
  • 封装 - 隐藏实现细节,只暴露必要接口
  • 命名空间 - 避免名称冲突
  • 可重用性 - 促进代码复用
  • 可测试性 - 更容易为独立模块编写测试

Rust模块系统的核心概念

概念 描述 关键字/文件
模块(Module) 代码的命名空间单元 mod
包(Package) 一个或多个crate的集合 Cargo.toml
Crate 编译单元,生成库或可执行文件 lib.rs/main.rs
工作空间(Workspace) 多个相关包的集合 [workspace]

4.2 mod层级设计构建代码树

模块声明与组织

Rust中的模块可以嵌套形成树状结构

// lib.rs
mod front_desk;    // 前台模块
mod cataloging;    // 编目模块
mod user_services; // 用户服务模块

// 嵌套模块示例
mod user_services {
    mod registration {
        // 用户注册相关功能
    }
    
    mod borrowing {
        // 借阅相关功能
    }
}

模块文件组织策略

Rust提供两种组织模块的方式

  1. 内联模块:在同一文件中定义

    // lib.rs
    mod config {
        pub const MAX_BOOKS: usize = 5;
    
        pub fn get_loan_duration() -> u32 {
            14 // 默认借阅期为14天
        }
    }
    
  2. 文件模块:在单独文件中定义

    lib.rs          // 声明模块
    └── config.rs   // 实现模块
    
    // lib.rs
    mod config; // 声明模块实现在config.rs中
    
    // config.rs
    pub const MAX_BOOKS: usize = 5;
    
    pub fn get_loan_duration() -> u32 {
        14
    }
    

子模块文件组织

对于更复杂的模块结构,可以使用目录:

lib.rs                 // 声明顶层模块
├── user_services.rs   // 声明user_services子模块
└── user_services/     // 子模块目录
    ├── registration.rs
    └── borrowing.rs
// user_services.rs
pub mod registration; // 声明子模块
pub mod borrowing;    // 声明子模块

模块路径与引用

使用use关键字简化模块路径:

// 不使用use
fn main() {
    crate::user_services::registration::register_new_user("Alice");
}

// 使用use
use crate::user_services::registration::register_new_user;

fn main() {
    register_new_user("Alice");
}

路径别名

use crate::user_services::registration::register_new_user as register;

fn main() {
    register("Alice");
}

批量导入

use crate::user_services::registration::{register_new_user, validate_email};

4.3 pub权限控制接口与实现分离

可见性规则

Rust中的所有项默认是私有的使用pub关键字使其公开:

mod library {
    pub struct Book {  // 公开结构体
        pub title: String,  // 公开字段
        isbn: String,       // 私有字段
    }
    
    impl Book {
        pub fn new(title: &str, isbn: &str) -> Self {  // 公开方法
            Self {
                title: title.to_string(),
                isbn: isbn.to_string(),
            }
        }
        
        fn validate_isbn(&self) -> bool {  // 私有方法
            !self.isbn.is_empty()
        }
    }
}

可见性级别

Rust提供多种可见性级别

关键字 可见范围 用途
(默认) 仅当前模块及其子模块 隐藏实现细节
pub 任何引入该模块的代码 公开API
pub(crate) 仅当前crate 内部API
pub(super) 仅父模块 限制子模块暴露
pub(in path) 指定路径 精细控制可见性

示例:

mod library {
    pub(crate) struct Catalog {  // 仅在crate内可见
        // ...
    }
    
    pub(super) fn internal_helper() {  // 仅对父模块可见
        // ...
    }
}

接口设计最佳实践

  1. 最小公开原则只公开必要的API
  2. 抽象泄漏控制:使用私有字段防止实现细节泄漏
  3. API稳定性:谨慎公开可能变化的内容
// 良好的接口设计
pub struct Library {
    // 私有实现细节
    books: Vec<Book>,
    users: HashMap<UserId, User>,
}

impl Library {
    // 公开稳定API
    pub fn new() -> Self { /* ... */ }
    pub fn add_book(&mut self, book: Book) { /* ... */ }
    pub fn borrow_book(&mut self, user_id: UserId, book_id: BookId) -> Result<(), BorrowError> { /* ... */ }
    
    // 私有辅助函数
    fn find_available_copy(&self, book_id: BookId) -> Option<&Book> { /* ... */ }
}

4.4 workspace多crate管理扩展到大型项目

工作空间基础

工作空间允许管理多个相关包:

# Cargo.toml (根目录)
[workspace]
members = [
    "library-core",
    "library-api",
    "library-cli"
]

目录结构:

library-workspace/
├── Cargo.toml       # 工作空间配置
├── library-core/    # 核心功能crate
├── library-api/     # API服务crate
└── library-cli/     # 命令行工具crate

工作空间依赖管理

工作空间内的crate可以相互依赖

# library-api/Cargo.toml
[dependencies]
library-core = { path = "../library-core" }

工作空间命令

在工作空间根目录执行命令会应用到所有成员:

# 构建所有crate
cargo build

# 测试特定crate
cargo test -p library-core

# 运行特定crate
cargo run -p library-cli

共享依赖优化

工作空间中的共享依赖只编译一次,提高构建效率:

# 在各crate的Cargo.toml中
[dependencies]
serde = "1.0"  # 在多个crate中使用的依赖只编译一次

4.5 文档测试:代码即文档

文档注释类型

Rust支持两种文档注释

  1. 外部文档注释:用于项的文档

    /// 表示图书馆中的一本书
    /// 
    /// # 示例
    /// ```
    /// let book = Book::new("Rust编程", "978-1234567890");
    /// assert_eq!(book.title(), "Rust编程");
    /// ```
    pub struct Book {
        // ...
    }
    
  2. 内部文档注释用于模块或crate整体文档

    //! # 图书馆管理系统
    //! 
    //! 这个模块提供图书馆管理的核心功能,包括:
    //! - 图书编目
    //! - 用户管理
    //! - 借阅流程
    

文档测试

文档中的代码块默认会作为测试运行:

/// 计算借阅的逾期费用
/// 
/// # 参数
/// * `days_overdue` - 逾期天数
/// 
/// # 返回值
/// 应付的逾期费用(元)
/// 
/// # 示例
/// ```
/// use library::calculate_overdue_fee;
/// assert_eq!(calculate_overdue_fee(5), 5.0);  // 5天逾期收费5元
/// assert_eq!(calculate_overdue_fee(0), 0.0);  // 未逾期,无费用
/// ```
pub fn calculate_overdue_fee(days_overdue: u32) -> f64 {
    days_overdue as f64
}

运行文档测试:

cargo test --doc

文档测试特殊标记

控制文档测试行为:

/// ```
/// # use library::Book;  // 这行在文档中隐藏,但测试时执行
/// let book = Book::new("Rust编程", "978-1234567890");
/// ```
///
/// ```no_run
/// // 编译但不运行的代码
/// let book = fetch_from_database("978-1234567890");
/// ```
///
/// ```compile_fail
/// // 应该编译失败的代码
/// let book: Book = "不是书";
/// ```
///
/// ```ignore
/// // 完全忽略的代码
/// let x = 非法语法;
/// ```

4.6 实例项目:图书馆管理系统

让我们应用所学知识,构建一个模块化的图书馆管理系统。

项目结构设计

library/
├── Cargo.toml
└── src/
    ├── lib.rs         # 主模块和公共API
    ├── models/        # 数据模型
    │   ├── mod.rs
    │   ├── book.rs
    │   └── user.rs
    ├── services/      # 业务逻辑
    │   ├── mod.rs
    │   ├── catalog.rs
    │   └── lending.rs
    └── utils/         # 工具函数
        ├── mod.rs
        └── validation.rs

实现核心模块

1. 主模块 (lib.rs)

//! # 图书馆管理系统
//! 
//! 一个模块化的图书馆管理系统,提供图书编目、用户管理和借阅服务。

pub mod models;
pub mod services;
mod utils;  // 内部工具模块,不对外暴露

// 重新导出核心类型,简化用户导入
pub use models::{Book, User};
pub use services::{catalog::Catalog, lending::LendingService};

/// 系统版本号
pub const VERSION: &str = "0.1.0";

/// 创建一个新的图书馆实例
pub fn new_library() -> services::Library {
    services::Library::new()
}

2. 模型模块 (models/mod.rs)

//! 系统中使用的数据模型

pub mod book;
pub mod user;

pub use book::Book;
pub use user::User;

3. 图书模型 (models/book.rs)

//! 图书相关的数据模型

use std::fmt;

/// 表示图书馆中的一本书
#[derive(Debug, Clone, PartialEq)]
pub struct Book {
    title: String,
    author: String,
    isbn: String,
    available: bool,
}

impl Book {
    /// 创建一本新书
    ///
    /// # 示例
    /// ```
    /// use library::Book;
    /// let book = Book::new("Rust编程艺术", "张三", "978-1234567890");
    /// assert_eq!(book.title(), "Rust编程艺术");
    /// assert!(book.is_available());
    /// ```
    pub fn new(title: &str, author: &str, isbn: &str) -> Self {
        Self {
            title: title.to_string(),
            author: author.to_string(),
            isbn: isbn.to_string(),
            available: true,
        }
    }
    
    /// 获取书名
    pub fn title(&self) -> &str {
        &self.title
    }
    
    /// 获取作者
    pub fn author(&self) -> &str {
        &self.author
    }
    
    /// 获取ISBN
    pub fn isbn(&self) -> &str {
        &self.isbn
    }
    
    /// 检查书是否可借
    pub fn is_available(&self) -> bool {
        self.available
    }
    
    /// 标记为已借出
    pub(crate) fn mark_borrowed(&mut self) {
        self.available = false;
    }
    
    /// 标记为已归还
    pub(crate) fn mark_returned(&mut self) {
        self.available = true;
    }
}

impl fmt::Display for Book {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} by {} (ISBN: {})", self.title, self.author, self.isbn)
    }
}

**4. 用户模型 (models/user.rs)**

```rust
//! 用户相关的数据模型

use std::fmt;

/// 用户ID类型
pub type UserId = u32;

/// 表示图书馆的一名用户
#[derive(Debug, Clone, PartialEq)]
pub struct User {
    id: UserId,
    name: String,
    email: String,
    active: bool,
    borrowed_books: Vec<String>, // 存储已借阅书籍的ISBN
}

impl User {
    /// 创建一个新用户
    ///
    /// # 示例
    /// ```
    /// use library::User;
    /// let user = User::new(1, "李四", "lisi@example.com");
    /// assert_eq!(user.name(), "李四");
    /// assert!(user.is_active());
    /// ```
    pub fn new(id: UserId, name: &str, email: &str) -> Self {
        Self {
            id,
            name: name.to_string(),
            email: email.to_string(),
            active: true,
            borrowed_books: Vec::new(),
        }
    }
    
    /// 获取用户ID
    pub fn id(&self) -> UserId {
        self.id
    }
    
    /// 获取用户名
    pub fn name(&self) -> &str {
        &self.name
    }
    
    /// 获取用户邮箱
    pub fn email(&self) -> &str {
        &self.email
    }
    
    /// 检查用户是否激活
    pub fn is_active(&self) -> bool {
        self.active
    }
    
    /// 获取用户已借阅的书籍ISBN列表
    pub fn borrowed_books(&self) -> &[String] {
        &self.borrowed_books
    }
    
    /// 记录用户借阅了一本书
    pub(crate) fn borrow_book(&mut self, isbn: &str) {
        self.borrowed_books.push(isbn.to_string());
    }
    
    /// 记录用户归还了一本书
    pub(crate) fn return_book(&mut self, isbn: &str) -> bool {
        if let Some(pos) = self.borrowed_books.iter().position(|b| b == isbn) {
            self.borrowed_books.remove(pos);
            true
        } else {
            false
        }
    }
    
    /// 停用用户账户
    pub(crate) fn deactivate(&mut self) {
        self.active = false;
    }
    
    /// 重新激活用户账户
    pub(crate) fn activate(&mut self) {
        self.active = true;
    }
}

impl fmt::Display for User {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} (ID: {}, Email: {})", self.name, self.id, self.email)
    }
}

5. 服务模块 (services/mod.rs)

//! 图书馆业务服务模块

pub mod catalog;
pub mod lending;

use std::collections::HashMap;
use crate::models::{Book, User, UserId};

/// 图书馆主服务
pub struct Library {
    catalog: catalog::Catalog,
    lending: lending::LendingService,
}

impl Library {
    /// 创建一个新的图书馆实例
    pub fn new() -> Self {
        Self {
            catalog: catalog::Catalog::new(),
            lending: lending::LendingService::new(),
        }
    }
    
    /// 获取目录服务
    pub fn catalog(&self) -> &catalog::Catalog {
        &self.catalog
    }
    
    /// 获取可变目录服务
    pub fn catalog_mut(&mut self) -> &mut catalog::Catalog {
        &mut self.catalog
    }
    
    /// 获取借阅服务
    pub fn lending(&self) -> &lending::LendingService {
        &self.lending
    }
    
    /// 获取可变借阅服务
    pub fn lending_mut(&mut self) -> &mut lending::LendingService {
        &mut self.lending
    }
}

impl Default for Library {
    fn default() -> Self {
        Self::new()
    }
}

6. 目录服务 (services/catalog.rs)

//! 图书目录管理服务

use std::collections::HashMap;
use crate::models::Book;
use crate::utils::validation;

/// 图书目录错误类型
#[derive(Debug)]
pub enum CatalogError {
    /// ISBN已存在
    DuplicateIsbn(String),
    /// ISBN不存在
    IsbnNotFound(String),
    /// ISBN格式无效
    InvalidIsbn(String),
}

/// 图书目录服务
pub struct Catalog {
    books: HashMap<String, Book>,
}

impl Catalog {
    /// 创建一个新的目录服务
    pub fn new() -> Self {
        Self {
            books: HashMap::new(),
        }
    }
    
    /// 添加一本新书到目录
    ///
    /// # 错误
    /// 如果ISBN已存在或无效返回错误
    ///
    /// # 示例
    /// ```
    /// use library::{Book, services::catalog::Catalog};
    /// 
    /// let mut catalog = Catalog::new();
    /// let book = Book::new("Rust编程", "张三", "978-1234567890");
    /// 
    /// // 首次添加成功
    /// assert!(catalog.add_book(book.clone()).is_ok());
    /// 
    /// // 重复添加失败
    /// assert!(catalog.add_book(book).is_err());
    /// ```
    pub fn add_book(&mut self, book: Book) -> Result<(), CatalogError> {
        let isbn = book.isbn();
        
        // 验证ISBN格式
        if !validation::is_valid_isbn(isbn) {
            return Err(CatalogError::InvalidIsbn(isbn.to_string()));
        }
        
        // 检查ISBN是否已存在
        if self.books.contains_key(isbn) {
            return Err(CatalogError::DuplicateIsbn(isbn.to_string()));
        }
        
        // 添加图书
        self.books.insert(isbn.to_string(), book);
        Ok(())
    }
    
    /// 根据ISBN查找图书
    pub fn find_by_isbn(&self, isbn: &str) -> Option<&Book> {
        self.books.get(isbn)
    }
    
    /// 根据ISBN查找可变图书引用
    pub(crate) fn find_by_isbn_mut(&mut self, isbn: &str) -> Option<&mut Book> {
        self.books.get_mut(isbn)
    }
    
    /// 根据标题查找图书
    pub fn find_by_title(&self, title: &str) -> Vec<&Book> {
        self.books.values()
            .filter(|book| book.title().contains(title))
            .collect()
    }
    
    /// 根据作者查找图书
    pub fn find_by_author(&self, author: &str) -> Vec<&Book> {
        self.books.values()
            .filter(|book| book.author().contains(author))
            .collect()
    }
    
    /// 获取所有图书
    pub fn all_books(&self) -> Vec<&Book> {
        self.books.values().collect()
    }
    
    /// 获取可用图书数量
    pub fn available_count(&self) -> usize {
        self.books.values()
            .filter(|book| book.is_available())
            .count()
    }
    
    /// 获取总图书数量
    pub fn total_count(&self) -> usize {
        self.books.len()
    }
}

impl Default for Catalog {
    fn default() -> Self {
        Self::new()
    }
}

7. 借阅服务 (services/lending.rs)

//! 图书借阅管理服务

use std::collections::HashMap;
use crate::models::{Book, User, UserId};
use crate::services::catalog::Catalog;

/// 借阅记录
#[derive(Debug)]
pub struct LoanRecord {
    user_id: UserId,
    isbn: String,
    loan_date: chrono::DateTime<chrono::Utc>,
    due_date: chrono::DateTime<chrono::Utc>,
}

/// 借阅错误类型
#[derive(Debug)]
pub enum LendingError {
    /// 用户不存在
    UserNotFound(UserId),
    /// 图书不存在
    BookNotFound(String),
    /// 图书不可用(已借出)
    BookUnavailable(String),
    /// 用户账户未激活
    UserInactive(UserId),
    /// 用户已达到最大借阅限制
    BorrowLimitReached(UserId),
    /// 借阅记录不存在
    LoanNotFound,
}

/// 借阅服务
pub struct LendingService {
    users: HashMap<UserId, User>,
    active_loans: Vec<LoanRecord>,
    max_loans_per_user: usize,
    loan_duration_days: i64,
}

impl LendingService {
    /// 创建一个新的借阅服务
    pub fn new() -> Self {
        Self {
            users: HashMap::new(),
            active_loans: Vec::new(),
            max_loans_per_user: 5,
            loan_duration_days: 14,
        }
    }
    
    /// 设置每位用户的最大借阅数量
    pub fn set_max_loans_per_user(&mut self, max: usize) {
        self.max_loans_per_user = max;
    }
    
    /// 设置借阅期限(天数)
    pub fn set_loan_duration(&mut self, days: i64) {
        self.loan_duration_days = days;
    }
    
    /// 添加用户
    pub fn add_user(&mut self, user: User) {
        self.users.insert(user.id(), user);
    }
    
    /// 查找用户
    pub fn find_user(&self, user_id: UserId) -> Option<&User> {
        self.users.get(&user_id)
    }
    
    /// 查找可变用户引用
    fn find_user_mut(&mut self, user_id: UserId) -> Option<&mut User> {
        self.users.get_mut(&user_id)
    }
    
    /// 借阅图书
    pub fn borrow_book(
        &mut self, 
        user_id: UserId, 
        isbn: &str, 
        catalog: &mut Catalog
    ) -> Result<(), LendingError> {
        // 检查用户是否存在
        let user = self.find_user_mut(user_id)
            .ok_or(LendingError::UserNotFound(user_id))?;
        
        // 检查用户是否激活
        if !user.is_active() {
            return Err(LendingError::UserInactive(user_id));
        }
        
        // 检查用户是否达到借阅上限
        if user.borrowed_books().len() >= self.max_loans_per_user {
            return Err(LendingError::BorrowLimitReached(user_id));
        }
        
        // 检查图书是否存在并可借
        let book = catalog.find_by_isbn_mut(isbn)
            .ok_or_else(|| LendingError::BookNotFound(isbn.to_string()))?;
        
        if !book.is_available() {
            return Err(LendingError::BookUnavailable(isbn.to_string()));
        }
        
        // 更新图书状态
        book.mark_borrowed();
        
        // 更新用户借阅记录
        user.borrow_book(isbn);
        
        // 创建借阅记录
        let now = chrono::Utc::now();
        let due_date = now + chrono::Duration::days(self.loan_duration_days);
        
        self.active_loans.push(LoanRecord {
            user_id,
            isbn: isbn.to_string(),
            loan_date: now,
            due_date,
        });
        
        Ok(())
    }
    
    /// 归还图书
    pub fn return_book(
        &mut self, 
        user_id: UserId, 
        isbn: &str, 
        catalog: &mut Catalog
    ) -> Result<(), LendingError> {
        // 检查用户是否存在
        let user = self.find_user_mut(user_id)
            .ok_or(LendingError::UserNotFound(user_id))?;
        
        // 检查图书是否存在
        let book = catalog.find_by_isbn_mut(isbn)
            .ok_or_else(|| LendingError::BookNotFound(isbn.to_string()))?;
        
        // 更新用户借阅记录
        if !user.return_book(isbn) {
            return Err(LendingError::LoanNotFound);
        }
        
        // 更新图书状态
        book.mark_returned();
        
        // 移除借阅记录
        let loan_index = self.active_loans.iter()
            .position(|loan| loan.user_id == user_id && loan.isbn == isbn)
            .ok_or(LendingError::LoanNotFound)?;
        
        self.active_loans.remove(loan_index);
        
        Ok(())
    }
    
    /// 获取用户的所有借阅
    pub fn get_user_loans(&self, user_id: UserId) -> Vec<&LoanRecord> {
        self.active_loans.iter()
            .filter(|loan| loan.user_id == user_id)
            .collect()
    }
    
    /// 获取所有逾期借阅
    pub fn get_overdue_loans(&self) -> Vec<&LoanRecord> {
        let now = chrono::Utc::now();
        self.active_loans.iter()
            .filter(|loan| loan.due_date < now)
            .collect()
    }
}

impl Default for LendingService {
    fn default() -> Self {
        Self::new()
    }
}

8. 工具模块 (utils/mod.rs)

//! 内部工具函数

pub mod validation;

9. 验证工具 (utils/validation.rs)

//! 数据验证工具

/// 检查ISBN是否有效
/// 
/// 简化版实现,仅检查基本格式
pub fn is_valid_isbn(isbn: &str) -> bool {
    // 移除所有连字符和空格
    let isbn = isbn.replace(['-', ' '], "");
    
    // ISBN-13应为13位数字
    if isbn.len() != 13 {
        return false;
    }
    
    // 检查是否全为数字
    isbn.chars().all(|c| c.is_ascii_digit())
}

/// 检查邮箱格式是否有效
pub fn is_valid_email(email: &str) -> bool {
    // 简化版邮箱验证
    email.contains('@') && email.split('@').count() == 2
}

#[cfg(test)]
mod tests {
    use super::*;
    
    #[test]
    fn test_valid_isbn() {
        assert!(is_valid_isbn("978-1234567890"));
        assert!(is_valid_isbn("9781234567890"));
    }
    
    #[test]
    fn test_invalid_isbn() {
        assert!(!is_valid_isbn("978-12345"));  // 太短
        assert!(!is_valid_isbn("978-1234567890X"));  // 包含非数字
    }
    
    #[test]
    fn test_valid_email() {
        assert!(is_valid_email("user@example.com"));
    }
    
    #[test]
    fn test_invalid_email() {
        assert!(!is_valid_email("user@"));
        assert!(!is_valid_email("user"));
        assert!(!is_valid_email("user@domain@example"));
    }
}

示例应用程序

让我们创建一个简单的命令行应用程序,展示如何使用我们的图书馆管理系统:

10. 主程序 (main.rs)

//! 图书馆管理系统示例应用

use library::{Book, User, new_library};

fn main() {
    println!("=== 图书馆管理系统 ===");
    
    // 创建图书馆实例
    let mut library = new_library();
    
    // 添加图书
    let books = [
        Book::new("Rust编程艺术", "张三", "978-1234567890"),
        Book::new("系统设计实践", "李四", "978-0987654321"),
        Book::new("数据结构与算法", "王五", "978-5432109876"),
    ];
    
    for book in &books {
        match library.catalog_mut().add_book(book.clone()) {
            Ok(_) => println!("添加图书: {}", book),
            Err(e) => println!("添加图书失败: {:?}", e),
        }
    }
    
    // 添加用户
    let users = [
        User::new(1, "赵六", "zhaoliu@example.com"),
        User::new(2, "钱七", "qianqi@example.com"),
    ];
    
    for user in &users {
        library.lending_mut().add_user(user.clone());
        println!("添加用户: {}", user);
    }
    
    // 借阅图书
    let user_id = 1;
    let isbn = "978-1234567890";
    
    match library.lending_mut().borrow_book(user_id, isbn, library.catalog_mut()) {
        Ok(_) => println!("用户 {} 成功借阅图书 {}", user_id, isbn),
        Err(e) => println!("借阅失败: {:?}", e),
    }
    
    // 查看用户借阅情况
    if let Some(user) = library.lending().find_user(user_id) {
        println!("用户 {} 已借阅的图书:", user.name());
        for isbn in user.borrowed_books() {
            if let Some(book) = library.catalog().find_by_isbn(isbn) {
                println!("  - {}", book);
            }
        }
    }
    
    // 归还图书
    match library.lending_mut().return_book(user_id, isbn, library.catalog_mut()) {
        Ok(_) => println!("用户 {} 成功归还图书 {}", user_id, isbn),
        Err(e) => println!("归还失败: {:?}", e),
    }
    
    // 查看图书馆统计信息
    println!("\n=== 图书馆统计 ===");
    println!("总图书数: {}", library.catalog().total_count());
    println!("可借图书数: {}", library.catalog().available_count());
}

项目配置

11. 包配置 (Cargo.toml)

[package]
name = "library"
version = "0.1.0"
edition = "2021"
authors = ["Your Name <your.email@example.com>"]
description = "A modular library management system"

[dependencies]
chrono = "0.4"

[dev-dependencies]
criterion = "0.5"

[lib]
name = "library"
path = "src/lib.rs"

[[bin]]
name = "library-cli"
path = "src/main.rs"

4.7 总结

在本章中我们探索了Rust的模块系统学习了如何组织和结构化大型项目。我们讨论了以下关键概念

  1. 模块层级设计:使用mod关键字创建模块树,通过文件系统组织代码
  2. 可见性控制:使用pub及其变体控制API的暴露范围
  3. 工作空间管理使用工作空间组织多个相关crate
  4. 文档测试:编写可执行的文档,确保代码示例始终有效

通过图书馆管理系统的实例项目,我们实践了这些概念,构建了一个模块化、可维护的系统。这种结构化方法不仅提高了代码的可读性和可维护性,还促进了团队协作和代码重用。

关键要点回顾

  • 使用模块组织相关功能,形成清晰的代码结构
  • 遵循"最小公开原则"只暴露必要的API
  • 利用文件系统组织模块,使代码结构直观
  • 使用工作空间管理大型项目中的多个crate
  • 编写文档测试,确保文档与代码同步

实践建议

  1. 模块设计:先设计模块结构,再编写实现代码
  2. API设计:谨慎考虑哪些项应该公开,哪些应该保持私有
  3. 文档优先:编写文档和测试,然后实现功能
  4. 渐进式重构:随着项目增长,不断调整模块结构

在下一章中我们将探索Rust的并发安全特性学习如何利用所有权系统编写高效、安全的并发代码。