1203 lines
28 KiB
Markdown
1203 lines
28 KiB
Markdown
# 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-2:pub 可见性
|
||
|
||
```rust
|
||
mod garden {
|
||
// 填空:使 plant 函数能够被模块外部访问
|
||
________ fn plant() -> &'static str {
|
||
"玫瑰花"
|
||
}
|
||
|
||
fn water() {
|
||
println!("浇水...");
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let flower = garden::plant();
|
||
println!("种了{}", flower);
|
||
|
||
// 下面这行取消注释会怎样?
|
||
// garden::water();
|
||
}
|
||
// 期望输出:种了玫瑰花
|
||
```
|
||
|
||
### 题目 2-3:use 关键字
|
||
|
||
```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-4:super 关键字
|
||
|
||
```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-5:as 别名
|
||
|
||
```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-7:pub 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 和 c(c 不存在)
|
||
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() {
|
||
// 方式 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()`:
|
||
```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>
|