28 KiB
Rust Package、Crate、Module 练习题
建议先手动写出每道题的答案,再运行代码验证。部分题目涉及多文件组织,请实际创建对应目录和文件来练习。
目录
一、概念辨析题
判断以下说法是否正确,并简要说明原因。
题目 1-1
一个 Package 只能包含一个 Crate。
题目 1-2
main.rs和lib.rs分别对应二进制 Crate 和库 Crate 的根文件。
题目 1-3
在模块树中,父模块默认可以访问子模块中的私有项。
题目 1-4
使用
use关键字将模块或项引入作用域后,其子模块也会自动被引入。
题目 1-5
cargo new my_project默认会创建一个二进制 Crate。
题目 1-6
同一个文件中可以定义多个
mod模块。
题目 1-7
pub use的作用完全等同于use,只是书写风格不同。
题目 1-8
在一个 Package 中,
src/bin/目录下的每个.rs文件都会被编译为一个独立的二进制 Crate。
二、填空题:补充代码
补全下列代码使其能通过编译并达到期望输出。
题目 2-1:定义模块
// 填空:定义一个名为 network 的模块,内部包含一个名为 connect 的函数
________ network {
fn connect() {
println!("已连接");
}
}
fn main() {
// 填空:调用 network 模块中的 connect 函数
network::________();
}
// 期望输出:已连接
题目 2-2:pub 可见性
mod garden {
// 填空:使 plant 函数能够被模块外部访问
________ fn plant() -> &'static str {
"玫瑰花"
}
fn water() {
println!("浇水...");
}
}
fn main() {
let flower = garden::plant();
println!("种了{}", flower);
// 下面这行取消注释会怎样?
// garden::water();
}
// 期望输出:种了玫瑰花
题目 2-3:use 关键字
mod front_of_house {
pub mod hosting {
pub fn add_to_waitlist() {
println!("已加入等候列表");
}
pub fn seat_at_table() {
println!("已入座");
}
}
}
fn main() {
// 填空:使用 use 将 hosting 模块引入当前作用域
________ front_of_house::hosting;
hosting::add_to_waitlist();
hosting::seat_at_table();
}
// 期望输出:
// 已加入等候列表
// 已入座
题目 2-4:super 关键字
fn serve_order() {
println!("上菜!");
}
mod back_of_house {
fn cook() {
println!("烹饪中...");
}
// 填空:使用 super 调用父模块中的 serve_order 函数
pub fn deliver() {
cook();
________::serve_order();
}
}
fn main() {
// 填空:正确调用 deliver 函数
________();
}
// 期望输出:
// 烹饪中...
// 上菜!
题目 2-5:as 别名
use std::fmt::Result as FmtResult;
// 填空:将 std::io::Result 以别名 IoResult 引入
use std::io::Result ________ ________;
fn main() {
// 直接使用别名调用 Ok 构造函数
let _r1: FmtResult = Ok(());
let _r2: IoResult<()> = Ok(());
println!("两种 Result 都能正常使用!");
}
题目 2-6:嵌套路径导入
// 下面的导入使用了 3 行 use 语句
// use std::cmp::Ordering;
// use std::io;
// use std::io::Write;
// 填空:替换为一行嵌套路径的 use 语句
use std::________;
use std::________::{self, Write};
fn main() {
println!("模块导入成功!");
}
题目 2-7:pub use 重导出
mod inner {
pub fn secret() -> &'static str {
"内部机密数据"
}
}
// 填空:将 inner::secret 重导出为公开接口,使外部可以通过 crate::config 访问
________ inner::secret as config;
fn main() {
// 填空:通过新的路径名调用
println!("{}", ________());
}
// 期望输出:内部机密数据
题目 2-8:模块拆分到文件
假设 src/main.rs 中有以下代码,需要将 garden 模块拆分到单独的文件中。
// ===== src/main.rs =====
mod garden;
fn main() {
garden::grow();
}
// 期望输出:植物正在生长...
// ===== 填空:写出 src/garden.rs 的内容 =====
________ fn grow() {
println!("植物正在生长...");
}
题目 2-9:模块拆分到目录
假设有以下模块结构,需要将 restaurant 模块拆分到 src/restaurant/ 目录中。
// ===== src/main.rs =====
mod restaurant;
fn main() {
restaurant::front::serve();
}
// 期望输出:上菜中...
// ===== 填空:写出需要的文件及其内容 =====
// 文件 1: src/restaurant/________
// 文件 2: src/restaurant/________
// restaurant/________ 内容:
pub mod front;
// restaurant/________ 内容:
pub fn serve() {
println!("上菜中...");
}
三、找出并修复错误
以下每段代码都有编译错误,请指出错误并写出修正后的代码。
题目 3-1
mod house {
fn open_door() {
println!("门开了");
}
}
fn main() {
house::open_door();
}
题目 3-2
mod front {
pub fn greet() {
println!("欢迎光临!");
}
}
fn main() {
use front;
greet();
}
题目 3-3
mod math {
pub mod operations {
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
}
}
fn main() {
use math::operations;
println!("{}", add(3, 5));
}
题目 3-4
mod a {
pub mod b {
pub fn hello() {
println!("hello from b");
}
}
}
fn main() {
use a::b;
// 想要同时使用模块 b 和 c(c 不存在)
use a::c;
b::hello();
}
题目 3-5
mod outer {
mod inner {
pub fn secret_data() -> &'static str {
"机密"
}
}
pub fn reveal() {
inner::secret_data();
}
}
fn main() {
outer::reveal();
// 下面这行是否可以?
// outer::inner::secret_data();
}
题目 3-6
mod food {
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("桃子"),
}
}
}
}
fn main() {
let mut meal = food::Breakfast::summer("黑麦面包");
meal.toast = String::from("全麦面包");
println!("我要{}吐司", meal.toast);
// 下面这行取消注释会怎样?
// meal.seasonal_fruit = String::from("蓝莓");
}
题目 3-7
// ===== Cargo.toml =====
// [package]
// name = "my_project"
// version = "0.1.0"
// edition = "2021"
// ===== src/main.rs =====
use rand::Rng;
fn main() {
let mut rng = rand::thread_rng();
let n: i32 = rng.gen_range(1..100);
println!("随机数: {}", n);
}
// 运行 cargo build 时报错:can't find crate for `rand`
题目 3-8
use std::collections::HashMap;
use std::collections::HashSet;
fn main() {
let mut map: HashMap<&str, i32> = HashMap::new();
map.insert("one", 1);
let mut set: HashSet<i32> = HashSet::new();
set.insert(1);
println!("HashMap: {:?}", map);
println!("HashSet: {:?}", set);
}
// 虽然编译通过,但如何将两个 use 语句合并为一行?
四、实战题:组织代码
题目 4-1:餐厅管理系统
请按以下要求组织一个餐厅管理系统的模块结构,并实现全部代码。
模块结构要求:
restaurant (库 Crate)
├── front_of_house/
│ ├── mod.rs → pub mod hosting; pub mod serving;
│ ├── hosting.rs → pub fn add_to_waitlist() { ... }
│ │ pub fn seat_at_table() { ... }
│ └── serving.rs → pub fn take_order() { ... }
│ pub fn serve_order() { ... }
│ pub fn take_payment() { ... }
├── back_of_house.rs → pub fn cook() { ... }
│ fn wash_dishes() { ... } ← 私有函数
│ pub fn clean_up() { ... } 调用 wash_dishes()
├── menu.rs → pub enum Dish { ... }
│ impl Dish { fn price(&self) -> f64 }
└── lib.rs → pub mod front_of_house;
pub mod back_of_house;
pub mod menu;
pub fn eat_at_restaurant() { 演示调用各模块 }
要求:
-
在
menu.rs中定义Dish枚举,包含以下变体和价格:Steak→ 168.0Salad→ 38.0Pasta→ 58.0Water→ 0.0(免费)
-
为
Dish实现price(&self) -> f64方法 -
实现
eat_at_restaurant()函数,演示完整的用餐流程:- 加入等候列表
- 入座
- 点菜(点一个牛排和一份沙拉)
- 上菜
- 结账(打印总价)
- 清理
-
创建对应的目录结构,写出所有文件的完整代码
题目 4-2:数学工具库
创建一个名为 math_tools 的库 Crate,按以下层级组织代码。
模块结构:
math_tools (库 Crate)
├── lib.rs
├── basic/
│ ├── mod.rs → pub mod arithmetic; pub mod compare;
│ ├── arithmetic.rs → pub fn add, sub, mul, div
│ └── compare.rs → pub fn min, max, is_even
├── advanced/
│ ├── mod.rs → pub mod stats; pub mod geometry;
│ ├── stats.rs → pub fn mean, median, variance
│ └── geometry.rs → pub fn circle_area, rect_area, triangle_area
└── utils.rs → pub fn is_prime(n: u32) -> bool
pub fn factorial(n: u32) -> u64
要求:
- 实现全部函数
lib.rs中使用pub use将以下函数重导出为库的顶层接口:add、mean、is_prime、circle_area
- 在
lib.rs中写测试(#[cfg(test)] mod tests)验证以下场景:add(2, 3) == 5mean(&[1.0, 2.0, 3.0, 4.0, 5.0]) == 3.0is_prime(17)和is_prime(18)circle_area(1.0)约等于 π
题目 4-3:多二进制 Crate 的 Package
创建一个 Package,包含一个库 Crate 和两个二进制 Crate。
结构:
my_app/
├── Cargo.toml → package name = "my_app"
├── src/
│ ├── lib.rs → 库 Crate:定义 Config 结构体,包含 host, port, debug 字段
│ │ 和 parse_args 函数,解析命令行参数
│ └── bin/
│ ├── server.rs → 二进制 Crate:启动一个模拟服务器,打印配置信息
│ └── client.rs → 二进制 Crate:模拟连接到服务器,打印连接信息
要求:
- 库中定义
Config结构体:pub struct Config { pub host: String, pub port: u16, pub debug: bool, } - 库中实现
Config的new关联函数(使用默认值) server.rs调用库的Config::new(),打印"服务器启动于 {host}:{port}",如果 debug 为 true 再打印调试信息client.rs调用库的Config::new(),打印"客户端连接到 {host}:{port}"- 创建完整的目录结构,写出所有文件内容
五、综合思考题
题目 5-1
分析以下模块结构。假设当前在 crate::outer::middle::inner 模块中,有哪些方式可以访问 crate::outer::top_level 中的 value 函数?请写出至少三种不同的路径写法。
// lib.rs
pub mod outer {
pub fn top_level() {}
pub mod middle {
pub mod inner {
pub fn deep() {
// 在此处调用 outer::top_level()
}
}
}
}
pub mod another {
pub fn value() {
println!("another value");
}
}
题目 5-2
对比以下三种 use 导入方式的优缺点和使用场景:
// 方式 A:导入模块本身
use std::collections;
// 使用: collections::HashMap::new()
// 方式 B:导入具体类型
use std::collections::HashMap;
// 使用: HashMap::new()
// 方式 C:通配符导入
use std::collections::*;
// 使用: HashMap::new(), HashSet::new(), BTreeMap::new()
题目 5-3
Rust 的可见性规则中,pub 的"公开"是相对于模块路径而言的,而不是公开给所有人。请解释以下代码中为什么 outer::inner::secret 虽然标记为 pub,但在 main 中仍然无法直接访问。
mod outer {
mod inner { // inner 模块本身是私有的
pub fn secret() {
println!("秘密");
}
}
pub fn reveal() {
inner::secret(); // 可以,因为 outer 是 inner 的父模块
}
}
fn main() {
outer::reveal(); // 可以
// outer::inner::secret(); // 取消注释会怎样?
}
题目 5-4
阅读以下代码,回答:
mod parent {
pub fn parent_func() {
println!("父函数");
}
pub mod child {
pub fn child_func() {
// 如何在 child 模块中调用 parent_func()?
}
}
}
mod sibling {
pub fn sibling_func() {
// 如何在此处调用 parent::parent_func()?
}
}
- 在
child模块中调用parent_func()应该使用什么路径?(写出具体写法) - 在
sibling模块中调用parent_func()应该使用什么路径? super和crate分别指代什么?各自适用于什么场景?
题目 5-5
pub mod 和 pub use 有什么区别?以下两种写法分别适用于什么场景?
// 写法 A:嵌套模块 + 重导出
mod internal {
pub fn helper() {}
}
pub use internal::helper;
// 写法 B:先定义公开模块,再导入使用
pub mod api {
pub fn helper() {}
}
参考答案
请独立完成再查看答案。
点击展开答案
一、概念辨析题
1-1:❌ 错误。一个 Package 最多包含一个库 Crate,但可以包含任意多个二进制 Crate。例如 src/main.rs 是一个二进制 Crate,src/bin/ 下的每个文件也是独立的二进制 Crate,同时还可以有一个 src/lib.rs 作为库 Crate。
1-2:✅ 正确。src/main.rs 是二进制 Crate 的根文件(crate root),src/lib.rs 是库 Crate 的根文件。Cargo 会将根文件传递给 rustc 来构建 crate。
1-3:❌ 错误。在模块树中,子模块可以访问父模块中的所有项(包括私有项),但反过来不成立——父模块不能访问子模块中的私有项。要将子模块的项暴露给外部,必须用 pub 标记。
1-4:❌ 错误。use 只引入指定的路径,不会自动引入子模块。例如 use std::collections; 只引入 collections 模块本身,collections 下的 HashMap 等仍需要通过 collections::HashMap 访问。
1-5:✅ 正确。cargo new my_project 创建的模板包含 src/main.rs,是一个二进制 Crate。cargo new my_lib --lib 创建的是库 Crate(src/lib.rs)。
1-6:✅ 正确。同一个文件中可以定义任意多个 mod 块,每个块成为一个独立的模块。
1-7:❌ 错误。use 将路径引入当前作用域供自己使用,外部代码无法感知。pub use 是重导出(re-exporting),不仅引入当前作用域,还将其作为当前模块的公开 API 暴露给外部使用者。
1-8:✅ 正确。src/bin/ 目录下的每个 .rs 文件会被 Cargo 自动编译为独立的二进制 Crate,名称等于文件名(不含扩展名)。
二、填空题
2-1:
mod network {
fn connect() {
println!("已连接");
}
}
fn main() {
network::connect();
}
2-2:
mod garden {
pub fn plant() -> &'static str {
"玫瑰花"
}
fn water() { println!("浇水..."); }
}
fn main() {
let flower = garden::plant();
println!("种了{}", flower);
// garden::water(); 取消注释会报错:water 是私有函数
}
2-3:
use front_of_house::hosting;
2-4:
pub fn deliver() {
cook();
super::serve_order();
}
fn main() {
back_of_house::deliver();
}
2-5:
use std::io::Result as IoResult;
2-6:
use std::{cmp::Ordering, io::{self, Write}};
2-7:
pub use inner::secret as config;
fn main() {
println!("{}", config());
}
2-8:
文件: src/garden.rs
pub fn grow() {
println!("植物正在生长...");
}
2-9:
文件 1: src/restaurant/mod.rs
内容: pub mod front;
文件 2: src/restaurant/front.rs
内容: pub fn serve() { println!("上菜中..."); }
三、修复错误
3-1:open_door 是私有函数,模块外部无法访问。修复:加 pub。
mod house {
pub fn open_door() { println!("门开了"); }
}
3-2:use front; 只引入了模块,没有引入函数。函数名本身不在作用域中。修复:
use front::greet;
// 或 use front; 后调用 front::greet();
3-3:use math::operations; 引入的是模块,add 需要通过 operations::add() 调用。修复:
use math::operations::add;
println!("{}", add(3, 5));
3-4:模块 a::c 不存在。如果 c 不需要,直接删除该行。如果想引入两个模块,应等模块存在。此题故意制造了一个引入不存在的模块的错误。
3-5:主函数中 outer::inner::secret_data() 不能使用。虽然 secret_data 标记了 pub,但 inner 模块本身是私有的(没有 pub mod),外部无法通过私有模块访问其内部的任何项。修复:将 mod inner 改为 pub mod inner。
3-6:取消注释会报错,因为 seasonal_fruit 字段是私有的,即便结构体实例是 mut,也不能修改私有字段。只有直接拥有该结构体的模块才能访问私有字段。但如果改为:
meal.seasonal_fruit = String::from("蓝莓");
编译错误:seasonal_fruit is private。外模块只能修改 pub 字段。
3-7:Cargo.toml 中未添加 rand 依赖。修复:在 [dependencies] 下添加:
[dependencies]
rand = "0.8"
然后运行 cargo build,Cargo 会自动下载并编译 rand。
3-8:合并为:
use std::collections::{HashMap, HashSet};
四、实战题
4-1 餐厅管理系统:
文件结构:
src/
├── lib.rs
├── front_of_house/
│ ├── mod.rs
│ ├── hosting.rs
│ └── serving.rs
├── back_of_house.rs
└── menu.rs
// src/lib.rs
pub mod front_of_house;
pub mod back_of_house;
pub mod menu;
use menu::Dish;
pub fn eat_at_restaurant() {
front_of_house::hosting::add_to_waitlist();
front_of_house::hosting::seat_at_table();
let order = vec![Dish::Steak, Dish::Salad];
front_of_house::serving::take_order(order.clone());
front_of_house::serving::serve_order();
let total: f64 = order.iter().map(|d| d.price()).sum();
println!("总消费: {:.2} 元", total);
front_of_house::serving::take_payment();
back_of_house::clean_up();
}
// 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/front_of_house/serving.rs
use crate::menu::Dish;
pub fn take_order(dishes: Vec<Dish>) {
print!("点菜: ");
for (i, dish) in dishes.iter().enumerate() {
if i > 0 { print!(", "); }
print!("{:?}", dish);
}
println!();
}
pub fn serve_order() {
println!("上菜完成!");
}
pub fn take_payment() {
println!("结账完成!");
}
// src/back_of_house.rs
pub fn cook() {
println!("烹饪中...");
}
fn wash_dishes() {
println!("洗碗中...");
}
pub fn clean_up() {
cook();
println!("厨房已清理");
wash_dishes();
}
// src/menu.rs
#[derive(Debug, Clone)]
pub enum Dish {
Steak,
Salad,
Pasta,
Water,
}
impl Dish {
pub fn price(&self) -> f64 {
match self {
Dish::Steak => 168.0,
Dish::Salad => 38.0,
Dish::Pasta => 58.0,
Dish::Water => 0.0,
}
}
}
4-2 数学工具库:
文件结构:
src/
├── lib.rs
├── basic/
│ ├── mod.rs
│ ├── arithmetic.rs
│ └── compare.rs
├── advanced/
│ ├── mod.rs
│ ├── stats.rs
│ └── geometry.rs
└── utils.rs
// src/lib.rs
pub mod basic;
pub mod advanced;
pub mod utils;
pub use basic::arithmetic::add;
pub use advanced::stats::mean;
pub use advanced::geometry::circle_area;
pub use utils::is_prime;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_add() {
assert_eq!(add(2, 3), 5);
}
#[test]
fn test_mean() {
assert_eq!(mean(&[1.0, 2.0, 3.0, 4.0, 5.0]), 3.0);
}
#[test]
fn test_is_prime() {
assert!(is_prime(17));
assert!(!is_prime(18));
}
#[test]
fn test_circle_area() {
let area = circle_area(1.0);
assert!((area - std::f64::consts::PI).abs() < 1e-10);
}
}
// src/basic/mod.rs
pub mod arithmetic;
pub mod compare;
// src/basic/arithmetic.rs
pub fn add(a: i32, b: i32) -> i32 { a + b }
pub fn sub(a: i32, b: i32) -> i32 { a - b }
pub fn mul(a: i32, b: i32) -> i32 { a * b }
pub fn div(a: i32, b: i32) -> Option<i32> {
if b == 0 { None } else { Some(a / b) }
}
// src/basic/compare.rs
pub fn min(a: i32, b: i32) -> i32 { if a < b { a } else { b } }
pub fn max(a: i32, b: i32) -> i32 { if a > b { a } else { b } }
pub fn is_even(n: i32) -> bool { n % 2 == 0 }
// src/advanced/mod.rs
pub mod stats;
pub mod geometry;
// src/advanced/stats.rs
pub fn mean(data: &[f64]) -> f64 {
let sum: f64 = data.iter().sum();
sum / data.len() as f64
}
pub fn median(data: &mut [f64]) -> f64 {
data.sort_by(|a, b| a.partial_cmp(b).unwrap());
let mid = data.len() / 2;
if data.len() % 2 == 0 {
(data[mid - 1] + data[mid]) / 2.0
} else {
data[mid]
}
}
pub fn variance(data: &[f64]) -> f64 {
let m = mean(data);
data.iter().map(|x| (x - m).powi(2)).sum::<f64>() / data.len() as f64
}
// src/advanced/geometry.rs
use std::f64::consts::PI;
pub fn circle_area(radius: f64) -> f64 { PI * radius * radius }
pub fn rect_area(width: f64, height: f64) -> f64 { width * height }
pub fn triangle_area(base: f64, height: f64) -> f64 { base * height / 2.0 }
// src/utils.rs
pub fn is_prime(n: u32) -> bool {
if n < 2 { return false; }
for i in 2..=((n as f64).sqrt() as u32) {
if n % i == 0 { return false; }
}
true
}
pub fn factorial(n: u32) -> u64 {
(1..=n).fold(1, |acc, x| acc * x as u64)
}
4-3 多二进制 Crate Package:
文件结构:
my_app/
├── Cargo.toml
└── src/
├── lib.rs
└── bin/
├── server.rs
└── client.rs
# Cargo.toml
[package]
name = "my_app"
version = "0.1.0"
edition = "2021"
// src/lib.rs
pub struct Config {
pub host: String,
pub port: u16,
pub debug: bool,
}
impl Config {
pub fn new() -> Config {
Config {
host: String::from("127.0.0.1"),
port: 8080,
debug: false,
}
}
}
// src/bin/server.rs
use my_app::Config;
fn main() {
let config = Config::new();
println!("服务器启动于 {}:{}", config.host, config.port);
if config.debug {
println!("[调试模式] 详细日志已启用");
}
}
// src/bin/client.rs
use my_app::Config;
fn main() {
let config = Config::new();
println!("客户端连接到 {}:{}", config.host, config.port);
}
运行方式:
cargo run --bin server # 启动服务器
cargo run --bin client # 启动客户端
五、综合思考题
5-1:在 inner::deep() 中访问 outer::top_level() 的三种方式:
pub fn deep() {
// 方式 1:super(回到父模块 middle,再回到 outer)
super::super::top_level();
// 方式 2:crate 绝对路径
crate::outer::top_level();
// 方式 3:self + 多级 super
// self::super::super::top_level(); // 等效于 super::super
}
若要访问 another::value():
super::super::super::another::value(); // 不够优雅
crate::another::value(); // 推荐:使用 crate 根路径
5-2:
| 维度 | 方式 A(导入模块) | 方式 B(导入类型) | 方式 C(通配符) |
|---|---|---|---|
| 清晰度 | 中等(知道来自哪个模块) | 低(看不出来自哪个模块) | 最低(完全不清楚来源) |
| 简洁度 | 低(写路径长) | 高(直接使用类型名) | 高 |
| 名称冲突风险 | 低 | 中等(可能与其他同名类型冲突) | 高(极易引入未知冲突) |
| 适用场景 | 需要明确模块来源时;模块下类型少时 | 高频使用的具体类型 | 测试模块、prelude 模式;生产代码不推荐 |
最佳实践:
- 常规代码:使用方式 A(模块导入),调用时写
collections::HashMap - 高频类型:使用方式 B
- 避免方式 C(除测试模块和 prelude 设计)
5-3:
虽然 secret 标记为 pub,但 inner 模块本身是私有的(mod inner 没有 pub)。Rust 的可见性是分层级的:父模块定义子模块的可见范围。因为 inner 模块对外不可见,即使它内部的项全是 pub,外部也无法访问——路径在 inner 这一层就已经堵死了。
类比:一个没有挂牌的办公楼(私有模块),即使一楼大堂对所有访客开放(内部函数是 pub),路人也找不到入口。
5-4:
-
在
child中调用parent_func():super::parent_func(); -
在
sibling中调用parent_func():crate::parent::parent_func(); -
super和crate的区别:super:指向当前模块的父模块,适用于在同级模块间或访问父模块内容时使用。crate:指向crate 根目录(即lib.rs或main.rs顶层),适用于跨模块树访问时使用绝对路径。- 场景:父子关系简单、层级少时用
super;跨多层级或需要稳定路径时用crate。
5-5:
写法 A:内部模块 + 重导出
mod internal {
pub fn helper() {}
}
pub use internal::helper;
internal模块是私有的,外部不知道它的存在- 通过
pub use将helper提升到外层模块的公开 API 中 - 适用于:内部组织代码但对外隐藏实现细节。外部调用者只看到
helper,不知道它来自internal模块。后续可以自由重构internal而不会破坏 API。
写法 B:公开模块
pub mod api {
pub fn helper() {}
}
api模块整体暴露在 API 中- 外部使用时需要
api::helper(),模块结构成为 API 的一部分 - 适用于:模块结构本身就是设计意图的一部分,希望用户理解模块层级。但将模块布局暴露后,修改结构可能破坏下游代码。
总结:
- 写法 A(
pub use重导出):灵活、可卸载、隐藏内部结构;库设计常用模式 - 写法 B(
pub mod):简单直接、但模块结构成为契约的一部分,重构成本高