模块系统
模块系统是 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()
}
}
小结
本章我们学习了:
- 包和 Crate:Cargo 项目和模块树的概念
- 模块定义:使用
mod组织代码 - 路径:绝对路径、相对路径、
super、self - 可见性:
pub控制公开和私有 - use 关键字:创建快捷方式、
as重命名、嵌套路径 - 模块文件分离:单文件和多文件模块组织方式
练习
- 创建一个库 crate,包含多个模块,实践可见性控制
- 使用
use组织标准库的引入,尝试嵌套路径语法 - 创建一个包含公开 API 和私有实现细节的模块