跳到主要内容

模块系统

模块系统是 Rust 管理代码组织和可见性的机制。通过模块,可以将代码分组、控制可见性、避免命名冲突。

包和 Crate

基本概念

  • 包(Package):一个 Cargo 项目,包含 Cargo.toml 文件
  • Crate:一个模块树,产生库或可执行文件
  • 模块(Module):组织代码、控制私有性
  • 路径(Path):访问模块中项的方式
包(Package)
└── Crate(模块树)
└── 模块(Module)
└── 项(函数、结构体等)

Crate 根

  • 库 crate 根src/lib.rs
  • 二进制 crate 根src/main.rs
my_project/
├── Cargo.toml
└── src/
├── lib.rs # 库 crate 根
├── main.rs # 二进制 crate 根
└── bin/ # 其他二进制文件
└── extra.rs

模块定义

mod 关键字

使用 mod 定义模块:

mod front_of_house {
mod hosting {
fn add_to_waitlist() {}
fn seat_at_table() {}
}

mod serving {
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
}
}

fn main() {
// 模块默认私有,外部无法访问
}

模块树结构

crate
└── front_of_house
├── hosting
│ ├── add_to_waitlist
│ └── seat_at_table
└── serving
├── take_order
├── serve_order
└── take_payment

路径

绝对路径和相对路径

mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("添加到等待列表");
}
}
}

pub fn eat_at_restaurant() {
// 绝对路径:从 crate 根开始
crate::front_of_house::hosting::add_to_waitlist();

// 相对路径:从当前模块开始
front_of_house::hosting::add_to_waitlist();
}

fn main() {
eat_at_restaurant();
}

super 关键字

super 用于引用父模块:

fn deliver_order() {
println!("配送订单");
}

mod back_of_house {
fn fix_incorrect_order() {
cook_order();
// 使用 super 访问父模块
super::deliver_order();
}

fn cook_order() {
println!("烹饪订单");
}
}

self 关键字

self 引用当前模块:

mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}

fn main() {
// self 引用当前模块
self::front_of_house::hosting::add_to_waitlist();
}

可见性

pub 关键字

默认情况下,模块和项是私有的。使用 pub 使其公开:

mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("添加到等待列表");
}

// 私有函数
fn internal_function() {
println!("内部函数");
}
}

// 私有模块
mod private_module {
pub fn public_in_private() {
println!("私有模块中的公开函数仍然无法外部访问");
}
}
}

pub fn eat_at_restaurant() {
// 公开模块中的公开函数可以访问
front_of_house::hosting::add_to_waitlist();

// 错误:private_module 是私有的
// front_of_house::private_module::public_in_private();
}

结构体可见性

结构体的字段有独立的可见性:

mod back_of_house {
pub struct Breakfast {
pub toast: String, // 公开字段
seasonal_fruit: String, // 私有字段
}

impl Breakfast {
// 公开的关联函数(构造器模式)
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("桃子"),
}
}
}
}

pub fn eat_at_restaurant() {
// 使用关联函数创建实例
let mut meal = back_of_house::Breakfast::summer("黑麦");

// 可以修改公开字段
meal.toast = String::from("小麦");
println!("选择了 {} 面包", meal.toast);

// 错误:私有字段无法访问
// meal.seasonal_fruit = String::from("蓝莓");
}

枚举可见性

枚举的变体与枚举本身具有相同可见性:

mod back_of_house {
pub enum Appetizer {
Soup, // 自动公开
Salad, // 自动公开
}
}

pub fn eat_at_restaurant() {
let order1 = back_of_house::Appetizer::Soup;
let order2 = back_of_house::Appetizer::Salad;
}

use 关键字

基本用法

使用 use 创建路径的快捷方式:

mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("添加到等待列表");
}
}
}

// 引入模块
use crate::front_of_house::hosting;

pub fn eat_at_restaurant() {
hosting::add_to_waitlist();
}

fn main() {
eat_at_restaurant();
}

引入函数

use crate::front_of_house::hosting::add_to_waitlist;

pub fn eat_at_restaurant() {
// 直接调用函数
add_to_waitlist();
}

习惯:引入函数时通常引入父模块,保留函数所属的上下文。

引入结构体和枚举

use std::collections::HashMap;

fn main() {
let mut map = HashMap::new();
map.insert(1, 2);
}

as 关键字

使用 as 重命名:

use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
Result::Ok(())
}

fn function2() -> IoResult<()> {
IoResult::Ok(())
}

嵌套路径

简化多个 use 语句:

// 传统写法
use std::io;
use std::io::Write;

// 嵌套路径写法
use std::io::{self, Write};

// 多个项
use std::collections::{HashMap, BTreeMap, HashSet};

// 从多个层级引入
use std::io::{self, Error, ErrorKind};

glob 运算符

引入所有公开项:

use std::collections::*;

fn main() {
let mut map = HashMap::new();
let mut set = HashSet::new();
}

注意:谨慎使用 glob,可能造成命名冲突。

模块文件分离

单文件模块

对于小模块,可以放在一个文件中:

// src/lib.rs
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {}
}
}

pub use front_of_house::hosting;

多文件模块

对于大型模块,可以分离到单独文件:

方式一:模块文件

src/
├── lib.rs
└── front_of_house.rs
// src/lib.rs
mod front_of_house; // 声明模块,Rust 会查找 front_of_house.rs

pub use front_of_house::hosting;
// src/front_of_house.rs
pub mod hosting {
pub fn add_to_waitlist() {
println!("添加到等待列表");
}
}

方式二:模块目录

src/
├── lib.rs
└── front_of_house/
├── mod.rs
└── hosting.rs
// src/lib.rs
mod front_of_house;
// src/front_of_house/mod.rs
pub mod hosting;
// src/front_of_house/hosting.rs
pub fn add_to_waitlist() {
println!("添加到等待列表");
}

完整示例

restaurant/
├── Cargo.toml
└── src/
├── lib.rs
├── main.rs
└── front_of_house/
├── mod.rs
├── hosting.rs
└── serving.rs
// src/lib.rs
pub mod front_of_house;

pub use front_of_house::hosting;
// src/front_of_house/mod.rs
pub mod hosting;
pub mod serving;
// src/front_of_house/hosting.rs
pub fn add_to_waitlist() {
println!("添加到等待列表");
}

pub fn seat_at_table() {
println!("安排入座");
}
// src/main.rs
use restaurant::front_of_house::hosting;

fn main() {
hosting::add_to_waitlist();
}

标准库常用模块

// 集合类型
use std::collections::{HashMap, HashSet, VecDeque, LinkedList, BTreeMap, BTreeSet};

// 输入输出
use std::io::{self, Read, Write, BufReader, BufWriter};
use std::fs::File;
use std::path::Path;

// 字符串处理
use std::fmt::{Display, Debug, Formatter, Result};

// 时间处理
use std::time::{Duration, Instant};

// 线程和同步
use std::thread;
use std::sync::{Mutex, Arc, RwLock};
use std::sync::mpsc::{channel, Sender, Receiver};

// 错误处理
use std::error::Error;
use std::result::Result;

// 迭代器
use std::iter::Iterator;

// 选项和结果
use std::option::Option;
use std::result::Result;

访问权限最佳实践

公开 API 设计

pub mod api {
// 公开的结构体
pub struct User {
pub id: u64, // 公开字段
pub username: String, // 公开字段
internal_id: u64, // 私有字段
}

impl User {
// 公开的构造函数
pub fn new(username: String) -> User {
User {
id: 0,
username,
internal_id: rand_id(),
}
}

// 公开方法
pub fn get_display_name(&self) -> &str {
&self.username
}

// 私有方法
fn validate(&self) -> bool {
!self.username.is_empty()
}
}

fn rand_id() -> u64 {
// 私有辅助函数
42
}
}

内部可变性模式

use std::cell::RefCell;

pub struct Counter {
count: RefCell<u32>, // 内部可变性
}

impl Counter {
pub fn new() -> Counter {
Counter {
count: RefCell::new(0),
}
}

pub fn increment(&self) { // 注意:&self 而不是 &mut self
*self.count.borrow_mut() += 1;
}

pub fn get_count(&self) -> u32 {
*self.count.borrow()
}
}

小结

本章我们学习了:

  1. 包和 Crate:Cargo 项目和模块树的概念
  2. 模块定义:使用 mod 组织代码
  3. 路径:绝对路径、相对路径、superself
  4. 可见性pub 控制公开和私有
  5. use 关键字:创建快捷方式、as 重命名、嵌套路径
  6. 模块文件分离:单文件和多文件模块组织方式

练习

  1. 创建一个库 crate,包含多个模块,实践可见性控制
  2. 使用 use 组织标准库的引入,尝试嵌套路径语法
  3. 创建一个包含公开 API 和私有实现细节的模块