learning-rust/part6/包和模块练习题.md

1203 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 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定义模块
```rust
// 填空:定义一个名为 network 的模块,内部包含一个名为 connect 的函数
________ network {
fn connect() {
println!("已连接");
}
}
fn main() {
// 填空:调用 network 模块中的 connect 函数
network::________();
}
// 期望输出:已连接
```
### 题目 2-2pub 可见性
```rust
mod garden {
// 填空:使 plant 函数能够被模块外部访问
________ fn plant() -> &'static str {
"玫瑰花"
}
fn water() {
println!("浇水...");
}
}
fn main() {
let flower = garden::plant();
println!("种了{}", flower);
// 下面这行取消注释会怎样?
// garden::water();
}
// 期望输出:种了玫瑰花
```
### 题目 2-3use 关键字
```rust
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-4super 关键字
```rust
fn serve_order() {
println!("上菜!");
}
mod back_of_house {
fn cook() {
println!("烹饪中...");
}
// 填空:使用 super 调用父模块中的 serve_order 函数
pub fn deliver() {
cook();
________::serve_order();
}
}
fn main() {
// 填空:正确调用 deliver 函数
________();
}
// 期望输出:
// 烹饪中...
// 上菜!
```
### 题目 2-5as 别名
```rust
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嵌套路径导入
```rust
// 下面的导入使用了 3 行 use 语句
// use std::cmp::Ordering;
// use std::io;
// use std::io::Write;
// 填空:替换为一行嵌套路径的 use 语句
use std::________;
use std::________::{self, Write};
fn main() {
println!("模块导入成功!");
}
```
### 题目 2-7pub use 重导出
```rust
mod inner {
pub fn secret() -> &'static str {
"内部机密数据"
}
}
// 填空:将 inner::secret 重导出为公开接口,使外部可以通过 crate::config 访问
________ inner::secret as config;
fn main() {
// 填空:通过新的路径名调用
println!("{}", ________());
}
// 期望输出:内部机密数据
```
### 题目 2-8模块拆分到文件
假设 `src/main.rs` 中有以下代码,需要将 `garden` 模块拆分到单独的文件中。
```rust
// ===== src/main.rs =====
mod garden;
fn main() {
garden::grow();
}
// 期望输出:植物正在生长...
// ===== 填空:写出 src/garden.rs 的内容 =====
________ fn grow() {
println!("植物正在生长...");
}
```
### 题目 2-9模块拆分到目录
假设有以下模块结构,需要将 `restaurant` 模块拆分到 `src/restaurant/` 目录中。
```rust
// ===== 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
```rust
mod house {
fn open_door() {
println!("门开了");
}
}
fn main() {
house::open_door();
}
```
### 题目 3-2
```rust
mod front {
pub fn greet() {
println!("欢迎光临!");
}
}
fn main() {
use front;
greet();
}
```
### 题目 3-3
```rust
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
```rust
mod a {
pub mod b {
pub fn hello() {
println!("hello from b");
}
}
}
fn main() {
use a::b;
// 想要同时使用模块 b 和 cc 不存在)
use a::c;
b::hello();
}
```
### 题目 3-5
```rust
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
```rust
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
```rust
// ===== 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
```rust
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() { 演示调用各模块 }
```
**要求:**
1.`menu.rs` 中定义 `Dish` 枚举,包含以下变体和价格:
- `Steak` → 168.0
- `Salad` → 38.0
- `Pasta` → 58.0
- `Water` → 0.0(免费)
2.`Dish` 实现 `price(&self) -> f64` 方法
3. 实现 `eat_at_restaurant()` 函数,演示完整的用餐流程:
- 加入等候列表
- 入座
- 点菜(点一个牛排和一份沙拉)
- 上菜
- 结账(打印总价)
- 清理
4. 创建对应的目录结构,写出所有文件的完整代码
### 题目 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
```
**要求:**
1. 实现全部函数
2. `lib.rs` 中使用 `pub use` 将以下函数重导出为库的顶层接口:
- `add`、`mean`、`is_prime`、`circle_area`
3.`lib.rs` 中写测试(`#[cfg(test)] mod tests`)验证以下场景:
- `add(2, 3) == 5`
- `mean(&[1.0, 2.0, 3.0, 4.0, 5.0]) == 3.0`
- `is_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模拟连接到服务器打印连接信息
```
**要求:**
1. 库中定义 `Config` 结构体:
```rust
pub struct Config {
pub host: String,
pub port: u16,
pub debug: bool,
}
```
2. 库中实现 `Config``new` 关联函数(使用默认值)
3. `server.rs` 调用库的 `Config::new()`,打印 `"服务器启动于 {host}:{port}"`,如果 debug 为 true 再打印调试信息
4. `client.rs` 调用库的 `Config::new()`,打印 `"客户端连接到 {host}:{port}"`
5. 创建完整的目录结构,写出所有文件内容
---
## 五、综合思考题
### 题目 5-1
分析以下模块结构。假设当前在 `crate::outer::middle::inner` 模块中,有哪些方式可以访问 `crate::outer::top_level` 中的 `value` 函数?请写出至少三种不同的路径写法。
```rust
// 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` 导入方式的优缺点和使用场景:
```rust
// 方式 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` 中仍然无法直接访问。
```rust
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
阅读以下代码,回答:
```rust
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()
}
}
```
1.`child` 模块中调用 `parent_func()` 应该使用什么路径?(写出具体写法)
2.`sibling` 模块中调用 `parent_func()` 应该使用什么路径?
3. `super``crate` 分别指代什么?各自适用于什么场景?
### 题目 5-5
`pub mod``pub use` 有什么区别?以下两种写法分别适用于什么场景?
```rust
// 写法 A嵌套模块 + 重导出
mod internal {
pub fn helper() {}
}
pub use internal::helper;
// 写法 B先定义公开模块再导入使用
pub mod api {
pub fn helper() {}
}
```
---
## 参考答案
> 请独立完成再查看答案。
<details>
<summary>点击展开答案</summary>
### 一、概念辨析题
**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**
```rust
mod network {
fn connect() {
println!("已连接");
}
}
fn main() {
network::connect();
}
```
**2-2**
```rust
mod garden {
pub fn plant() -> &'static str {
"玫瑰花"
}
fn water() { println!("浇水..."); }
}
fn main() {
let flower = garden::plant();
println!("种了{}", flower);
// garden::water(); 取消注释会报错water 是私有函数
}
```
**2-3**
```rust
use front_of_house::hosting;
```
**2-4**
```rust
pub fn deliver() {
cook();
super::serve_order();
}
fn main() {
back_of_house::deliver();
}
```
**2-5**
```rust
use std::io::Result as IoResult;
```
**2-6**
```rust
use std::{cmp::Ordering, io::{self, Write}};
```
**2-7**
```rust
pub use inner::secret as config;
fn main() {
println!("{}", config());
}
```
**2-8**
```
文件: src/garden.rs
```
```rust
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`
```rust
mod house {
pub fn open_door() { println!("门开了"); }
}
```
**3-2**`use front;` 只引入了模块,没有引入函数。函数名本身不在作用域中。修复:
```rust
use front::greet;
// 或 use front; 后调用 front::greet();
```
**3-3**`use math::operations;` 引入的是模块,`add` 需要通过 `operations::add()` 调用。修复:
```rust
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`,也不能修改私有字段。只有直接拥有该结构体的模块才能访问私有字段。但如果改为:
```rust
meal.seasonal_fruit = String::from("蓝莓");
```
编译错误:`seasonal_fruit` is private。外模块只能修改 `pub` 字段。
**3-7**`Cargo.toml` 中未添加 `rand` 依赖。修复:在 `[dependencies]` 下添加:
```toml
[dependencies]
rand = "0.8"
```
然后运行 `cargo build`Cargo 会自动下载并编译 `rand`
**3-8**:合并为:
```rust
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
```
```rust
// 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();
}
```
```rust
// src/front_of_house/mod.rs
pub mod hosting;
pub mod serving;
```
```rust
// src/front_of_house/hosting.rs
pub fn add_to_waitlist() {
println!("已加入等候列表");
}
pub fn seat_at_table() {
println!("已入座");
}
```
```rust
// 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!("结账完成!");
}
```
```rust
// src/back_of_house.rs
pub fn cook() {
println!("烹饪中...");
}
fn wash_dishes() {
println!("洗碗中...");
}
pub fn clean_up() {
cook();
println!("厨房已清理");
wash_dishes();
}
```
```rust
// 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
```
```rust
// 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);
}
}
```
```rust
// src/basic/mod.rs
pub mod arithmetic;
pub mod compare;
```
```rust
// 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) }
}
```
```rust
// 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 }
```
```rust
// src/advanced/mod.rs
pub mod stats;
pub mod geometry;
```
```rust
// 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
}
```
```rust
// 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 }
```
```rust
// 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
```
```toml
# Cargo.toml
[package]
name = "my_app"
version = "0.1.0"
edition = "2021"
```
```rust
// 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,
}
}
}
```
```rust
// src/bin/server.rs
use my_app::Config;
fn main() {
let config = Config::new();
println!("服务器启动于 {}:{}", config.host, config.port);
if config.debug {
println!("[调试模式] 详细日志已启用");
}
}
```
```rust
// 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()` 的三种方式:
```rust
pub fn deep() {
// 方式 1super回到父模块 middle再回到 outer
super::super::top_level();
// 方式 2crate 绝对路径
crate::outer::top_level();
// 方式 3self + 多级 super
// self::super::super::top_level(); // 等效于 super::super
}
```
若要访问 `another::value()`
```rust
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**
1.`child` 中调用 `parent_func()`
```rust
super::parent_func();
```
2.`sibling` 中调用 `parent_func()`
```rust
crate::parent::parent_func();
```
3. `super``crate` 的区别:
- `super`:指向**当前模块的父模块**,适用于在同级模块间或访问父模块内容时使用。
- `crate`:指向**crate 根目录**(即 `lib.rs``main.rs` 顶层),适用于跨模块树访问时使用绝对路径。
- 场景:父子关系简单、层级少时用 `super`;跨多层级或需要稳定路径时用 `crate`
**5-5**
**写法 A内部模块 + 重导出**
```rust
mod internal {
pub fn helper() {}
}
pub use internal::helper;
```
- `internal` 模块是私有的,外部不知道它的存在
- 通过 `pub use``helper` 提升到外层模块的公开 API 中
- 适用于:**内部组织代码但对外隐藏实现细节**。外部调用者只看到 `helper`,不知道它来自 `internal` 模块。后续可以自由重构 `internal` 而不会破坏 API。
**写法 B公开模块**
```rust
pub mod api {
pub fn helper() {}
}
```
- `api` 模块整体暴露在 API 中
- 外部使用时需要 `api::helper()`,模块结构成为 API 的一部分
- 适用于:**模块结构本身就是设计意图的一部分**,希望用户理解模块层级。但将模块布局暴露后,修改结构可能破坏下游代码。
总结:
- 写法 A`pub use` 重导出):灵活、可卸载、隐藏内部结构;库设计常用模式
- 写法 B`pub mod`):简单直接、但模块结构成为契约的一部分,重构成本高
</details>