1651 lines
38 KiB
Markdown
1651 lines
38 KiB
Markdown
# Rust 枚举(Enum)练习题
|
||
|
||
> 建议先手动写出每道题的答案,再运行代码验证。
|
||
|
||
## 目录
|
||
|
||
- [一、基础题:判断正误](#一基础题判断正误)
|
||
- [二、填空题:补充代码](#二填空题补充代码)
|
||
- [三、找出并修复错误](#三找出并修复错误)
|
||
- [四、编程题](#四编程题)
|
||
- [五、综合思考题](#五综合思考题)
|
||
- [参考答案](#参考答案)
|
||
|
||
***
|
||
|
||
## 一、基础题:判断正误
|
||
|
||
判断以下代码能否通过编译。如果可以,说明原因;如果不能,指出错误原因。
|
||
|
||
### 题目 1-1
|
||
|
||
```rust
|
||
enum Direction {
|
||
North,
|
||
South,
|
||
East,
|
||
West,
|
||
}
|
||
|
||
fn main() {
|
||
let d = Direction::North;
|
||
println!("{:?}", d);
|
||
}
|
||
```
|
||
|
||
> 不能,缺少#\[derive(Debug)]
|
||
|
||
### 题目 1-2
|
||
|
||
```rust
|
||
enum IpAddrKind {
|
||
V4,
|
||
V6,
|
||
}
|
||
|
||
struct IpAddr {
|
||
kind: IpAddrKind,
|
||
address: String,
|
||
}
|
||
|
||
fn main() {
|
||
let home = IpAddr {
|
||
kind: IpAddrKind::V4,
|
||
address: String::from("127.0.0.1"),
|
||
};
|
||
println!("home kind: {:?}, address: {}", home.kind, home.address);
|
||
}
|
||
```
|
||
|
||
> 不能,IpAddrKind缺少#\[derive(Debug)]
|
||
|
||
### 题目 1-3
|
||
|
||
```rust
|
||
enum IpAddr {
|
||
V4(String),
|
||
V6(String),
|
||
}
|
||
|
||
fn main() {
|
||
let home = IpAddr::V4(String::from("127.0.0.1"));
|
||
let loopback = IpAddr::V6(String::from("::1"));
|
||
}
|
||
```
|
||
|
||
> 可以通过
|
||
|
||
### 题目 1-4
|
||
|
||
```rust
|
||
fn main() {
|
||
let some_number = Some(5);
|
||
let some_string = Some("hello");
|
||
let absent_number: Option<i32> = None;
|
||
|
||
println!("{:?}", some_number);
|
||
println!("{:?}", some_string);
|
||
println!("{:?}", absent_number);
|
||
}
|
||
```
|
||
|
||
> 可以通过
|
||
|
||
### 题目 1-5
|
||
|
||
```rust
|
||
fn main() {
|
||
let x: Option<i32> = Some(5);
|
||
let y = x + 1;
|
||
println!("{}", y);
|
||
}
|
||
```
|
||
|
||
> 不能,Option枚举类型不能直接用+,需要实现add方法
|
||
|
||
### 题目 1-6
|
||
|
||
```rust
|
||
enum Coin {
|
||
Penny,
|
||
Nickel,
|
||
Dime,
|
||
Quarter,
|
||
}
|
||
|
||
fn value_in_cents(coin: Coin) -> u8 {
|
||
match coin {
|
||
Coin::Penny => 1,
|
||
Coin::Nickel => 5,
|
||
Coin::Dime => 10,
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
println!("{}", value_in_cents(Coin::Quarter));
|
||
}
|
||
```
|
||
|
||
> 不能,match需要匹配所有可能出现的类型
|
||
|
||
### 题目 1-7
|
||
|
||
```rust
|
||
enum Message {
|
||
Quit,
|
||
Move { x: i32, y: i32 },
|
||
Write(String),
|
||
ChangeColor(i32, i32, i32),
|
||
}
|
||
|
||
fn main() {
|
||
let m = Message::Write(String::from("hello"));
|
||
let n = Message::Move { x: 10, y: 20 };
|
||
let q = Message::ChangeColor(255, 0, 0);
|
||
}
|
||
```
|
||
|
||
> 可以
|
||
|
||
### 题目 1-8
|
||
|
||
```rust
|
||
enum Coin {
|
||
Penny,
|
||
Nickel,
|
||
Dime,
|
||
Quarter,
|
||
}
|
||
|
||
fn main() {
|
||
let coin = Coin::Dime;
|
||
let mut count = 0;
|
||
if let Coin::Quarter = coin {
|
||
println!("State quarter!");
|
||
} else {
|
||
count += 1;
|
||
}
|
||
println!("count: {}", count);
|
||
}
|
||
```
|
||
|
||
> 可以
|
||
|
||
***
|
||
|
||
## 二、填空题:补充代码
|
||
|
||
补全下列代码使其能通过编译并达到期望输出。
|
||
|
||
### 题目 2-1:定义简单枚举
|
||
|
||
```rust
|
||
// 填空:定义一个名为 TrafficLight 的枚举,包含 Red、Yellow、Green 三个变体
|
||
________ TrafficLight {
|
||
Red,
|
||
Yellow,
|
||
Green,
|
||
}
|
||
|
||
fn main() {
|
||
let light = TrafficLight::________;
|
||
match light {
|
||
TrafficLight::Red => println!("停止"),
|
||
TrafficLight::Yellow => println!("减速"),
|
||
TrafficLight::Green => println!("通行"),
|
||
}
|
||
// 期望输出:停止
|
||
}
|
||
```
|
||
|
||
```rust
|
||
enum TrafficLight {
|
||
Red,
|
||
Yellow,
|
||
Green,
|
||
}
|
||
|
||
fn main() {
|
||
let light = TrafficLight::Red;
|
||
match light {
|
||
TrafficLight::Red => println!("停止"),
|
||
TrafficLight::Yellow => println!("减速"),
|
||
TrafficLight::Green => println!("通行"),
|
||
}
|
||
}
|
||
```
|
||
|
||
### 题目 2-2:带数据的枚举
|
||
|
||
```rust
|
||
// 填空:定义一个名为 WebEvent 的枚举
|
||
// - PageLoad: 无数据
|
||
// - KeyPress: 包含一个 char
|
||
// - Click: 包含两个 i32(x 和 y 坐标)
|
||
enum WebEvent {
|
||
________,
|
||
KeyPress(char),
|
||
Click { x: i32, y: i32 },
|
||
}
|
||
|
||
fn main() {
|
||
let load = WebEvent::PageLoad;
|
||
let key = WebEvent::________('a');
|
||
let click = WebEvent::Click { x: 100, y: 200 };
|
||
println!("ok");
|
||
}
|
||
```
|
||
|
||
```rust
|
||
enum WebEvent {
|
||
PageLoad,
|
||
KeyPress(char),
|
||
Click { x: i32, y: i32 },
|
||
}
|
||
|
||
fn main() {
|
||
let load = WebEvent::PageLoad;
|
||
let key = WebEvent::KeyPress('a');
|
||
let click = WebEvent::Click { x: 100, y: 200 };
|
||
println!("ok");
|
||
}
|
||
```
|
||
|
||
### 题目 2-3:Option 的使用
|
||
|
||
```rust
|
||
fn main() {
|
||
let v1 = Some(10);
|
||
let v2: Option<i32> = ________;
|
||
|
||
// 填空:使用 match 安全地提取 Option 中的值,None 时返回 0
|
||
let val1 = match v1 {
|
||
Some(n) => n,
|
||
None => 0,
|
||
};
|
||
let val2 = match v2 {
|
||
Some(n) => n,
|
||
________ => 0,
|
||
};
|
||
println!("val1: {}, val2: {}", val1, val2);
|
||
// 期望输出:val1: 10, val2: 0
|
||
}
|
||
```
|
||
|
||
```rust
|
||
fn main() {
|
||
let v1 = Some(10);
|
||
let v2: Option<i32> = None;
|
||
|
||
let val1 = match v1 {
|
||
Some(n) => n,
|
||
None => 0,
|
||
};
|
||
let val2 = match v2 {
|
||
Some(n) => n,
|
||
None => 0,
|
||
};
|
||
println!("val1: {}, val2: {}", val1, val2);
|
||
}
|
||
```
|
||
|
||
### 题目 2-4:Result 的使用
|
||
|
||
```rust
|
||
fn divide(a: i32, b: i32) -> ________ {
|
||
if b == 0 {
|
||
Err(String::from("除数不能为零"))
|
||
} else {
|
||
________(a / b)
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let result1 = divide(10, 2);
|
||
let result2 = divide(10, 0);
|
||
|
||
// 填空:使用 match 处理 Result
|
||
match result1 {
|
||
Ok(val) => println!("结果: {}", val),
|
||
Err(e) => println!("错误: {}", e),
|
||
}
|
||
|
||
match result2 {
|
||
Ok(val) => println!("结果: {}", val),
|
||
________ => println!("错误: {}", e),
|
||
}
|
||
// 期望输出:
|
||
// 结果: 5
|
||
// 错误: 除数不能为零
|
||
}
|
||
```
|
||
|
||
```rust
|
||
fn divide(a: i32, b: i32) -> Result<i32, String> {
|
||
if b == 0 {
|
||
Err(String::from("除数不能为零"))
|
||
} else {
|
||
Ok(a / b)
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let result1 = divide(10, 2);
|
||
let result2 = divide(10, 0);
|
||
|
||
match result1 {
|
||
Ok(val) => println!("结果: {}", val),
|
||
Err(e) => println!("错误: {}", e),
|
||
}
|
||
|
||
match result2 {
|
||
Ok(val) => println!("结果: {}", val),
|
||
Err(e) => println!("错误: {}", e),
|
||
}
|
||
}
|
||
```
|
||
|
||
### 题目 2-5:枚举上的方法
|
||
|
||
```rust
|
||
enum Message {
|
||
Quit,
|
||
Write(String),
|
||
Move { x: i32, y: i32 },
|
||
}
|
||
|
||
impl Message {
|
||
// 填空:定义一个 call 方法,打印消息内容
|
||
fn ________(&self) {
|
||
match self {
|
||
Message::Quit => println!("退出"),
|
||
Message::Write(text) => println!("写入: {}", text),
|
||
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
|
||
}
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let m = Message::Write(String::from("你好世界"));
|
||
m.call(); // 期望输出:写入: 你好世界
|
||
|
||
let n = Message::Move { x: 10, y: 20 };
|
||
n.call(); // 期望输出:移动到 (10, 20)
|
||
}
|
||
```
|
||
|
||
```rust
|
||
enum Message {
|
||
Quit,
|
||
Write(String),
|
||
Move { x: i32, y: i32 },
|
||
}
|
||
|
||
impl Message {
|
||
fn call(&self) {
|
||
match self {
|
||
Message::Quit => println!("退出"),
|
||
Message::Write(text) => println!("写入: {}", text),
|
||
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
|
||
}
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let m = Message::Write(String::from("你好世界"));
|
||
m.call();
|
||
|
||
let n = Message::Move { x: 10, y: 20 };
|
||
n.call();
|
||
}
|
||
```
|
||
|
||
### 题目 2-6:if let 语法
|
||
|
||
```rust
|
||
fn main() {
|
||
let config_max = Some(3u8);
|
||
|
||
// 填空:使用 if let 简化 match
|
||
________ = config_max {
|
||
println!("最大值为: {}", max);
|
||
}
|
||
|
||
// 等价于以下 match 写法:
|
||
// match config_max {
|
||
// Some(max) => println!("最大值为: {}", max),
|
||
// _ => (),
|
||
// }
|
||
// 期望输出:最大值为: 3
|
||
}
|
||
```
|
||
|
||
```rust
|
||
fn main() {
|
||
let config_max = Some(3u8);
|
||
|
||
if let Some(max) = config_max {
|
||
println!("最大值为: {}", max);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 题目 2-7:嵌套 Option 处理
|
||
|
||
```rust
|
||
fn main() {
|
||
let opt: Option<Option<i32>> = Some(Some(42));
|
||
|
||
// 填空:使用模式匹配提取最内层的值
|
||
match opt {
|
||
Some(Some(inner)) => println!("值是: {}", ________),
|
||
Some(None) => println!("内层是 None"),
|
||
None => println!("外层是 None"),
|
||
}
|
||
// 期望输出:值是: 42
|
||
}
|
||
```
|
||
|
||
```rust
|
||
fn main() {
|
||
let opt: Option<Option<i32>> = Some(Some(42));
|
||
|
||
match opt {
|
||
Some(Some(inner)) => println!("值是: {}", inner),
|
||
Some(None) => println!("内层是 None"),
|
||
None => println!("外层是 None"),
|
||
}
|
||
}
|
||
```
|
||
|
||
### 题目 2-8:match 的多种模式
|
||
|
||
```rust
|
||
fn main() {
|
||
let x = 3;
|
||
|
||
// 填空:使用 match 的多模式匹配和范围匹配
|
||
match x {
|
||
________ => println!("是 1 或 2"),
|
||
3..=8 => println!("在 3 到 8 之间"),
|
||
_ => println!("其他值"),
|
||
}
|
||
// 期望输出:在 3 到 8 之间
|
||
}
|
||
```
|
||
|
||
```rust
|
||
fn main() {
|
||
let x = 3;
|
||
|
||
match x {
|
||
1 | 2 => println!("是 1 或 2"),
|
||
3..=8 => println!("在 3 到 8 之间"),
|
||
_ => println!("其他值"),
|
||
}
|
||
}
|
||
```
|
||
|
||
***
|
||
|
||
## 三、找出并修复错误
|
||
|
||
以下每段代码都有编译错误,请指出错误并写出修正后的代码。
|
||
|
||
### 题目 3-1
|
||
|
||
```rust
|
||
enum Status {
|
||
Active,
|
||
Inactive,
|
||
}
|
||
|
||
fn main() {
|
||
let s = Status::Active;
|
||
println!("{}", s);
|
||
}
|
||
```
|
||
|
||
```rust
|
||
#[derive(Debug)]
|
||
enum Status {
|
||
Active,
|
||
Inactive,
|
||
}
|
||
|
||
fn main() {
|
||
let s = Status::Active;
|
||
println!("{:?}", s);
|
||
}
|
||
```
|
||
|
||
### 题目 3-2
|
||
|
||
```rust
|
||
fn main() {
|
||
let x: i8 = 5;
|
||
let y: Option<i8> = Some(5);
|
||
|
||
let sum = x + y;
|
||
println!("{}", sum);
|
||
}
|
||
```
|
||
|
||
### 题目 3-3
|
||
|
||
```rust
|
||
enum Number {
|
||
Integer(i32),
|
||
Float(f64),
|
||
}
|
||
|
||
fn main() {
|
||
let n = Number::Integer(10);
|
||
|
||
match n {
|
||
Number::Integer => println!("整数: {}", 0),
|
||
Number::Float => println!("浮点数: {}", 0.0),
|
||
}
|
||
}
|
||
```
|
||
|
||
### 题目 3-4
|
||
|
||
```rust
|
||
#[derive(Debug)]
|
||
enum Coin {
|
||
Penny,
|
||
Nickel,
|
||
Dime,
|
||
}
|
||
|
||
fn main() {
|
||
let coin = Coin::Penny;
|
||
let val = match coin {
|
||
Coin::Penny => 1,
|
||
Coin::Nickel => 5,
|
||
};
|
||
println!("{}", val);
|
||
}
|
||
```
|
||
|
||
### 题目 3-5
|
||
|
||
```rust
|
||
enum Action {
|
||
Move(i32, i32),
|
||
Jump,
|
||
}
|
||
|
||
fn main() {
|
||
let action = Action::Move(10, 20);
|
||
|
||
if let Action::Move(x, y) = action {
|
||
println!("移动: x={}, y={}", x, y);
|
||
}
|
||
|
||
// 此处 action 的所有权是否还在?
|
||
match action {
|
||
Action::Jump => println!("跳跃"),
|
||
_ => (),
|
||
}
|
||
}
|
||
```
|
||
|
||
### 题目 3-6
|
||
|
||
```rust
|
||
enum Shape {
|
||
Circle(f64),
|
||
Rectangle(f64, f64),
|
||
}
|
||
|
||
fn area(shape: Shape) -> f64 {
|
||
match shape {
|
||
Shape::Circle(r) => 3.14 * r * r,
|
||
Shape::Rectangle(w, h) => w * h,
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let s = Shape::Circle(5.0);
|
||
println!("面积: {}", area(s));
|
||
println!("面积: {}", area(s)); // 第二次调用
|
||
}
|
||
```
|
||
|
||
### 题目 3-7
|
||
|
||
```rust
|
||
fn main() {
|
||
let numbers = vec![1, 2, 3, 4, 5];
|
||
let target = 10;
|
||
|
||
let result = numbers.iter().find(|&&x| x == target);
|
||
|
||
println!("找到: {}", result);
|
||
}
|
||
```
|
||
|
||
### 题目 3-8
|
||
|
||
```rust
|
||
enum Color {
|
||
Rgb(u8, u8, u8),
|
||
Named(String),
|
||
}
|
||
|
||
fn main() {
|
||
let c = Color::Rgb(255, 0, 0);
|
||
|
||
let color_str = match c {
|
||
Color::Rgb(r, g, b) => format!("rgb({}, {}, {})", r, g, b),
|
||
Color::Named(name) => name,
|
||
};
|
||
|
||
println!("{}", color_str);
|
||
}
|
||
```
|
||
|
||
***
|
||
|
||
## 四、编程题
|
||
|
||
### 题目 4-1:文件系统节点
|
||
|
||
设计一个枚举表示文件系统中的节点(文件或目录),并实现相应方法。
|
||
|
||
```rust
|
||
// 定义枚举 FsNode
|
||
// - File: 包含文件名(String)和文件大小(u64)
|
||
// - Directory: 包含目录名(String)和子节点列表(Vec<FsNode>)
|
||
|
||
// 你的代码:定义 FsNode 枚举
|
||
|
||
impl FsNode {
|
||
// 1. 获取节点名称
|
||
fn name(&self) -> &str {
|
||
// 你的代码
|
||
}
|
||
|
||
// 2. 计算节点占用的总大小
|
||
// 文件直接返回大小;目录递归累加所有子节点大小
|
||
fn total_size(&self) -> u64 {
|
||
// 你的代码
|
||
}
|
||
|
||
// 3. 打印文件树结构(带缩进)
|
||
// print_tree(&self, depth: usize)
|
||
// 每层缩进 2 个空格
|
||
fn print_tree(&self, depth: usize) {
|
||
// 你的代码
|
||
}
|
||
|
||
// 4. 统计文件数量(不含目录)
|
||
fn file_count(&self) -> usize {
|
||
// 你的代码
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let mut root = FsNode::Directory {
|
||
name: String::from("/"),
|
||
children: vec![
|
||
FsNode::File { name: String::from("readme.txt"), size: 1024 },
|
||
FsNode::Directory {
|
||
name: String::from("src"),
|
||
children: vec![
|
||
FsNode::File { name: String::from("main.rs"), size: 2048 },
|
||
FsNode::File { name: String::from("lib.rs"), size: 4096 },
|
||
],
|
||
},
|
||
FsNode::File { name: String::from("config.toml"), size: 512 },
|
||
],
|
||
};
|
||
|
||
root.print_tree(0);
|
||
println!("总大小: {} 字节", root.total_size());
|
||
println!("文件数量: {}", root.file_count());
|
||
|
||
// 期望输出:
|
||
// / (目录)
|
||
// readme.txt (1024 字节)
|
||
// src (目录)
|
||
// main.rs (2048 字节)
|
||
// lib.rs (4096 字节)
|
||
// config.toml (512 字节)
|
||
// 总大小: 7680 字节
|
||
// 文件数量: 3
|
||
}
|
||
```
|
||
|
||
### 题目 4-2:计算器
|
||
|
||
使用枚举实现一个简单的命令式计算器。
|
||
|
||
```rust
|
||
// 定义枚举 Operation
|
||
// - Add(i32, i32)
|
||
// - Subtract(i32, i32)
|
||
// - Multiply(i32, i32)
|
||
// - Divide(i32, i32)
|
||
|
||
// 你的代码:定义 Operation 枚举
|
||
|
||
impl Operation {
|
||
// 1. 执行运算并返回 Result<f64, String>
|
||
// 除法时检查除数是否为 0
|
||
fn execute(&self) -> Result<f64, String> {
|
||
// 你的代码
|
||
}
|
||
|
||
// 2. 返回该运算的符号字符串
|
||
fn symbol(&self) -> &str {
|
||
// 你的代码
|
||
}
|
||
|
||
// 3. 返回该运算的算式描述,如 "3 + 4"
|
||
fn describe(&self) -> String {
|
||
// 你的代码
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let ops = vec![
|
||
Operation::Add(10, 5),
|
||
Operation::Subtract(10, 5),
|
||
Operation::Multiply(10, 5),
|
||
Operation::Divide(10, 2),
|
||
Operation::Divide(10, 0),
|
||
];
|
||
|
||
for op in &ops {
|
||
print!("{} = ", op.describe());
|
||
match op.execute() {
|
||
Ok(result) => println!("{}", result),
|
||
Err(e) => println!("错误: {}", e),
|
||
}
|
||
}
|
||
|
||
// 期望输出:
|
||
// 10 + 5 = 15
|
||
// 10 - 5 = 5
|
||
// 10 * 5 = 50
|
||
// 10 / 2 = 5
|
||
// 10 / 0 = 错误: 除数不能为零
|
||
}
|
||
```
|
||
|
||
### 题目 4-3:JSON 值类型
|
||
|
||
定义一个枚举表示简化的 JSON 值类型。
|
||
|
||
```rust
|
||
// 定义枚举 JsonValue
|
||
// - Null
|
||
// - Bool(bool)
|
||
// - Number(f64)
|
||
// - String(String)
|
||
// - Array(Vec<JsonValue>)
|
||
// - Object: 包含一个 Vec<(String, JsonValue)> 作为键值对
|
||
|
||
// 你的代码:定义 JsonValue 枚举
|
||
|
||
impl JsonValue {
|
||
// 1. 返回 JSON 类型的名称字符串,如 "Null", "Bool" 等
|
||
fn type_name(&self) -> &str {
|
||
// 你的代码
|
||
}
|
||
|
||
// 2. 将 JsonValue 转为格式化的字符串(递归实现)
|
||
// 格式要求:
|
||
// - Null -> "null"
|
||
// - Bool(b) -> "true"/"false"
|
||
// - Number(n) -> 格式化数字(保留有意义的小数)
|
||
// - String(s) -> "\"s\""
|
||
// - Array(arr) -> "[元素1, 元素2, ...]"
|
||
// - Object(pairs) -> "{\n key: value,\n ...\n}"
|
||
fn to_string(&self) -> String {
|
||
// 你的代码
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let json = JsonValue::Object(vec![
|
||
(String::from("name"), JsonValue::String(String::from("张三"))),
|
||
(String::from("age"), JsonValue::Number(25.0)),
|
||
(String::from("is_student"), JsonValue::Bool(false)),
|
||
(String::from("scores"), JsonValue::Array(vec![
|
||
JsonValue::Number(85.5),
|
||
JsonValue::Number(92.0),
|
||
JsonValue::Number(78.5),
|
||
])),
|
||
(String::from("address"), JsonValue::Null),
|
||
]);
|
||
|
||
println!("类型: {}", json.type_name());
|
||
println!("{}", json.to_string());
|
||
|
||
// 期望输出(格式大致如下):
|
||
// 类型: Object
|
||
// {
|
||
// name: "张三",
|
||
// age: 25,
|
||
// is_student: false,
|
||
// scores: [85.5, 92, 78.5],
|
||
// address: null
|
||
// }
|
||
}
|
||
```
|
||
|
||
### 题目 4-4:状态机
|
||
|
||
使用枚举实现一个简单的订单状态机。
|
||
|
||
```rust
|
||
// 定义枚举 OrderState
|
||
// - Pending: 待支付
|
||
// - Paid { amount: f64 }: 已支付,记录支付金额
|
||
// - Shipped { tracking_id: String }: 已发货,记录快递单号
|
||
// - Delivered: 已签收
|
||
// - Cancelled { reason: String }: 已取消,记录取消原因
|
||
|
||
// 你的代码:定义 OrderState 枚举
|
||
|
||
impl OrderState {
|
||
// 1. 判断订单是否可以取消(只有 Pending 和 Paid 状态可以取消)
|
||
fn can_cancel(&self) -> bool {
|
||
// 你的代码
|
||
}
|
||
|
||
// 2. 支付订单(Pending -> Paid)
|
||
// 返回 Result<OrderState, String>
|
||
// 如果当前状态不是 Pending,返回错误
|
||
fn pay(self, amount: f64) -> Result<OrderState, String> {
|
||
// 你的代码
|
||
}
|
||
|
||
// 3. 发货(Paid -> Shipped)
|
||
// 返回 Result<OrderState, String>
|
||
// 如果当前状态不是 Paid,返回错误
|
||
fn ship(self, tracking_id: String) -> Result<OrderState, String> {
|
||
// 你的代码
|
||
}
|
||
|
||
// 4. 签收(Shipped -> Delivered)
|
||
// 返回 Result<OrderState, String>
|
||
fn deliver(self) -> Result<OrderState, String> {
|
||
// 你的代码
|
||
}
|
||
|
||
// 5. 取消订单,返回新的状态
|
||
fn cancel(self, reason: String) -> Result<OrderState, String> {
|
||
// 你的代码
|
||
}
|
||
|
||
// 6. 获取状态描述
|
||
fn describe(&self) -> String {
|
||
// 你的代码
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let order = OrderState::Pending;
|
||
println!("初始状态: {}", order.describe());
|
||
|
||
let order = match order.pay(299.0) {
|
||
Ok(state) => state,
|
||
Err(e) => { println!("{}", e); return; }
|
||
};
|
||
println!("支付后: {}", order.describe());
|
||
|
||
let order = match order.ship(String::from("SF1234567890")) {
|
||
Ok(state) => state,
|
||
Err(e) => { println!("{}", e); return; }
|
||
};
|
||
println!("发货后: {}", order.describe());
|
||
|
||
let order = match order.deliver() {
|
||
Ok(state) => state,
|
||
Err(e) => { println!("{}", e); return; }
|
||
};
|
||
println!("签收后: {}", order.describe());
|
||
|
||
// 测试异常路径:已签收的订单不能再取消
|
||
match order.cancel(String::from("不需要了")) {
|
||
Ok(_) => println!("取消成功"),
|
||
Err(e) => println!("取消失败: {}", e),
|
||
}
|
||
|
||
// 期望输出:
|
||
// 初始状态: 待支付
|
||
// 支付后: 已支付 299 元
|
||
// 发货后: 已发货 (SF1234567890)
|
||
// 签收后: 已签收
|
||
// 取消失败: 当前状态 已签收 不允许取消
|
||
}
|
||
```
|
||
|
||
### 题目 4-5:表达式求值器
|
||
|
||
使用枚举定义一个简单的数学表达式树并实现求值。
|
||
|
||
```rust
|
||
// 定义枚举 Expr
|
||
// - Number(f64): 数字字面量
|
||
// - Add(Box<Expr>, Box<Expr>): 加法
|
||
// - Subtract(Box<Expr>, Box<Expr>): 减法
|
||
// - Multiply(Box<Expr>, Box<Expr>): 乘法
|
||
// - Divide(Box<Expr>, Box<Expr>): 除法
|
||
// - Negate(Box<Expr>): 取负
|
||
|
||
// 你的代码:定义 Expr 枚举
|
||
|
||
impl Expr {
|
||
// 1. 求值,返回 Result<f64, String>
|
||
fn eval(&self) -> Result<f64, String> {
|
||
// 你的代码
|
||
}
|
||
|
||
// 2. 返回表达式的中缀表示字符串,包含必要的括号
|
||
// 例如:(1 + 2) * 3
|
||
fn to_string(&self) -> String {
|
||
// 你的代码
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
// 构建表达式: (3 + 4) * (10 - 2) / -5
|
||
let expr = Expr::Divide(
|
||
Box::new(Expr::Multiply(
|
||
Box::new(Expr::Add(
|
||
Box::new(Expr::Number(3.0)),
|
||
Box::new(Expr::Number(4.0)),
|
||
)),
|
||
Box::new(Expr::Subtract(
|
||
Box::new(Expr::Number(10.0)),
|
||
Box::new(Expr::Number(2.0)),
|
||
)),
|
||
)),
|
||
Box::new(Expr::Negate(Box::new(Expr::Number(5.0)))),
|
||
);
|
||
|
||
println!("表达式: {}", expr.to_string());
|
||
match expr.eval() {
|
||
Ok(result) => println!("结果: {}", result),
|
||
Err(e) => println!("错误: {}", e),
|
||
}
|
||
|
||
// 期望输出:
|
||
// 表达式: ((3 + 4) * (10 - 2)) / (-5)
|
||
// 结果: -11.2
|
||
}
|
||
```
|
||
|
||
***
|
||
|
||
## 五、综合思考题
|
||
|
||
### 题目 5-1
|
||
|
||
分析以下代码中 `match` 的匹配过程。`some_u8_value` 的值为 `3u8`,在 match 中匹配到了哪条分支?Rust 的 `|` 模式和 `..=` 范围模式分别适用于什么场景?
|
||
|
||
```rust
|
||
fn main() {
|
||
let some_u8_value = 3u8;
|
||
match some_u8_value {
|
||
1 => println!("one"),
|
||
2 | 3 | 4 => println!("two, three, or four"),
|
||
5..=10 => println!("five through ten"),
|
||
_ => println!("anything"),
|
||
}
|
||
}
|
||
```
|
||
|
||
### 题目 5-2
|
||
|
||
对比 `match`、`if let` 和 `matches!` 宏。在什么场景下分别应该使用哪种?以下三段代码分别适合用哪种方式改写?
|
||
|
||
```rust
|
||
// 场景 A:两个分支都需要处理
|
||
enum Coin { Penny, Nickel, Dime, Quarter }
|
||
fn value(coin: Coin) -> u8 {
|
||
match coin {
|
||
Coin::Penny => 1,
|
||
Coin::Nickel => 5,
|
||
Coin::Dime => 10,
|
||
Coin::Quarter => 25,
|
||
}
|
||
}
|
||
|
||
// 场景 B:只关心一种模式,其余忽略
|
||
let opt = Some(42);
|
||
// 打印 opt 中的值(如果存在)
|
||
|
||
// 场景 C:只需要布尔判断
|
||
let x = Some(5);
|
||
// 判断 x 是否为 Some 且值大于 3
|
||
```
|
||
|
||
### 题目 5-3
|
||
|
||
`Option<T>` 和 `Result<T, E>` 都用于处理"可能存在或不存在"的情况,但语义不同。请说明:
|
||
|
||
1. `Option<T>` 适用于什么场景?`Result<T, E>` 适用于什么场景?
|
||
2. 如何将 `Option<T>` 转换为 `Result<T, E>`?(写出一段示例代码)
|
||
3. 如何将 `Result<T, E>` 转换为 `Option<T>`?(写出一段示例代码)
|
||
|
||
### 题目 5-4
|
||
|
||
以下代码中,`msg` 在第一次 `match` 后和第二次 `match` 后分别发生了什么?为什么 Rust 需要 match 的所有权机制?
|
||
|
||
```rust
|
||
#[derive(Debug)]
|
||
enum Message {
|
||
Write(String),
|
||
Quit,
|
||
}
|
||
|
||
fn main() {
|
||
let msg = Message::Write(String::from("hello"));
|
||
|
||
match msg {
|
||
Message::Write(s) => println!("写入: {}", s),
|
||
Message::Quit => println!("退出"),
|
||
}
|
||
|
||
// println!("{:?}", msg); // 取消注释会怎样?为什么?
|
||
}
|
||
```
|
||
|
||
### 题目 5-5
|
||
|
||
Rust 没有传统的 `null`,而是用 `Option<T>`。分析以下两种设计的优劣:
|
||
|
||
```rust
|
||
// 设计 A:C 风格,用特殊值表示"无"
|
||
struct User {
|
||
name: String,
|
||
middle_name: String, // 没有中间名时用空字符串 ""
|
||
age: Option<u32>, // 未知年龄时用 None
|
||
}
|
||
|
||
// 设计 B:全部使用 Option
|
||
struct User {
|
||
name: String,
|
||
middle_name: Option<String>,
|
||
age: Option<u32>,
|
||
}
|
||
```
|
||
|
||
1. `middle_name` 在两种设计中分别如何表示"没有中间名"?
|
||
2. 哪种设计更符合 Rust 的惯用风格?为什么?
|
||
3. 如果用户同时有 `Option<String>` 和 `Option<String>` 类型的字段,它们各自为 None 的语义可能不同(如"未提供" vs "明确为空"),你会如何设计来区分这些情况?
|
||
|
||
***
|
||
|
||
## 参考答案
|
||
|
||
> 请独立完成再查看答案。
|
||
|
||
<details>
|
||
<summary>点击展开答案</summary>
|
||
|
||
### 一、基础题
|
||
|
||
**1-1**:❌ 编译错误。枚举变体不会自动实现任何 trait。需要 `#[derive(Debug)]` 或手动实现 `Debug` 才能使用 `{:?}` 打印。
|
||
|
||
**1-2**:❌ 编译错误。`IpAddrKind` 没有 derive `Debug`,所以 `{:?}` 无法打印 `home.kind`。需要加上 `#[derive(Debug)]`。
|
||
|
||
**1-3**:✅ 通过编译。枚举变体可以附带不同类型的数据,此例中 `V4` 和 `V6` 都附带 `String`。
|
||
|
||
**1-4**:✅ 通过编译。`Option<T>` 是 Rust 标准库预置的枚举,`Some` 和 `None` 可以直接使用,`Option` 实现了 `Debug`。`None` 需要类型标注因为它无法自动推断 `T` 的类型。
|
||
|
||
**1-5**:❌ 编译错误。`Option<i32>` 和 `i32` 是不同的类型,不能直接相加。需要先用 match 或 `unwrap()` 等方法提取值。
|
||
|
||
**1-6**:❌ 编译错误。match 必须穷尽所有可能(exhaustive)。`Coin` 有 4 个变体但只匹配了 3 个,缺少 `Coin::Quarter` 分支。需要补全或添加 `_ =>` 通配分支。
|
||
|
||
**1-7**:✅ 通过编译。枚举变体可以使用不同的数据绑定方式:无数据(`Quit`)、命名结构体式(`Move { x, y }`)、元组式(`Write(String)`、`ChangeColor(i32, i32, i32)`)。
|
||
|
||
**1-8**:✅ 通过编译。`if let` 是 `match` 的语法糖,只匹配一种模式并忽略其余。这里匹配失败走 `else` 分支,`count` 变为 1。
|
||
|
||
### 二、填空题
|
||
|
||
**2-1**:
|
||
|
||
```rust
|
||
enum TrafficLight {
|
||
Red,
|
||
Yellow,
|
||
Green,
|
||
}
|
||
|
||
let light = TrafficLight::Red;
|
||
```
|
||
|
||
**2-2**:
|
||
|
||
```rust
|
||
enum WebEvent {
|
||
PageLoad,
|
||
KeyPress(char),
|
||
Click { x: i32, y: i32 },
|
||
}
|
||
|
||
let key = WebEvent::KeyPress('a');
|
||
```
|
||
|
||
**2-3**:
|
||
|
||
```rust
|
||
let v2: Option<i32> = None;
|
||
|
||
let val2 = match v2 {
|
||
Some(n) => n,
|
||
None => 0,
|
||
};
|
||
```
|
||
|
||
**2-4**:
|
||
|
||
```rust
|
||
fn divide(a: i32, b: i32) -> Result<i32, String> {
|
||
if b == 0 {
|
||
Err(String::from("除数不能为零"))
|
||
} else {
|
||
Ok(a / b)
|
||
}
|
||
}
|
||
|
||
match result2 {
|
||
Ok(val) => println!("结果: {}", val),
|
||
Err(e) => println!("错误: {}", e),
|
||
}
|
||
```
|
||
|
||
**2-5**:
|
||
|
||
```rust
|
||
fn call(&self) {
|
||
match self {
|
||
Message::Quit => println!("退出"),
|
||
Message::Write(text) => println!("写入: {}", text),
|
||
Message::Move { x, y } => println!("移动到 ({}, {})", x, y),
|
||
}
|
||
}
|
||
```
|
||
|
||
**2-6**:
|
||
|
||
```rust
|
||
if let Some(max) = config_max {
|
||
println!("最大值为: {}", max);
|
||
}
|
||
```
|
||
|
||
**2-7**:
|
||
|
||
```rust
|
||
match opt {
|
||
Some(Some(inner)) => println!("值是: {}", inner),
|
||
Some(None) => println!("内层是 None"),
|
||
None => println!("外层是 None"),
|
||
}
|
||
```
|
||
|
||
**2-8**:
|
||
|
||
```rust
|
||
match x {
|
||
1 | 2 => println!("是 1 或 2"),
|
||
3..=8 => println!("在 3 到 8 之间"),
|
||
_ => println!("其他值"),
|
||
}
|
||
```
|
||
|
||
### 三、修复错误
|
||
|
||
**3-1**:`Display` trait 未实现。修复:
|
||
|
||
```rust
|
||
#[derive(Debug)]
|
||
enum Status { Active, Inactive }
|
||
|
||
fn main() {
|
||
let s = Status::Active;
|
||
println!("{:?}", s); // 使用 Debug 格式
|
||
}
|
||
```
|
||
|
||
或者手动实现 `Display`。
|
||
|
||
**3-2**:`Option<i8>` 和 `i8` 不能直接相加。修复:
|
||
|
||
```rust
|
||
fn main() {
|
||
let x: i8 = 5;
|
||
let y: Option<i8> = Some(5);
|
||
|
||
let sum = x + y.unwrap_or(0);
|
||
println!("{}", sum);
|
||
}
|
||
```
|
||
|
||
或用 match:
|
||
|
||
```rust
|
||
let sum = match y {
|
||
Some(val) => x + val,
|
||
None => x,
|
||
};
|
||
```
|
||
|
||
**3-3**:match 分支没有绑定内部的值。修复:
|
||
|
||
```rust
|
||
match n {
|
||
Number::Integer(v) => println!("整数: {}", v),
|
||
Number::Float(v) => println!("浮点数: {}", v),
|
||
}
|
||
```
|
||
|
||
**3-4**:match 不穷尽,缺少 `Coin::Dime` 分支。Rust 要求 match 覆盖所有可能。修复:
|
||
|
||
```rust
|
||
let val = match coin {
|
||
Coin::Penny => 1,
|
||
Coin::Nickel => 5,
|
||
Coin::Dime => 10,
|
||
// 或使用 _ => 10
|
||
};
|
||
```
|
||
|
||
**3-5**:✅ 编译通过。此题是陷阱——实际上能编译通过。`if let` 不会移动所有权(因为类型实现了 Copy 的话)。在这个例子中 `i32` 实现了 Copy,`Action::Move` 中的 `x` 和 `y` 是 i32 类型,所以 `if let` 只做了解构引用,不影响后续使用。
|
||
|
||
但如果 Action 包含非 Copy 类型(如 String),`if let` 会部分移动,导致后续不能使用 action。例如:
|
||
|
||
```rust
|
||
enum Action { Move(i32, String), Jump }
|
||
let action = Action::Move(10, String::from("up"));
|
||
if let Action::Move(x, s) = action { ... }
|
||
// match action { ... } ← 此时编译错误,因为 String 已被移出
|
||
```
|
||
|
||
**3-6**:`area(s)` 第一次调用后 `s` 的所有权被移入函数而消费,第二次调用时 `s` 已失效。修复:
|
||
|
||
- 将 `area` 改为 `area(shape: &Shape) -> f64`(借用),调用时传 `&s`
|
||
- 或在第一次调用前 `clone`
|
||
|
||
```rust
|
||
fn area(shape: &Shape) -> f64 {
|
||
match shape {
|
||
Shape::Circle(r) => 3.14 * r * r,
|
||
Shape::Rectangle(w, h) => w * h,
|
||
}
|
||
}
|
||
// 调用: area(&s)
|
||
```
|
||
|
||
**3-7**:`find` 返回 `Option<&i32>`,不是 `i32`。修复:
|
||
|
||
```rust
|
||
let result = numbers.iter().find(|&&x| x == target);
|
||
|
||
match result {
|
||
Some(v) => println!("找到: {}", v),
|
||
None => println!("未找到"),
|
||
}
|
||
```
|
||
|
||
**3-8**:✅ 通过编译。此题是陷阱——实际上编译通过。`format!` 返回 `String`,`name` 也是 `String`,返回类型一致。`match` 的两个分支都返回 `String`,类型匹配。
|
||
|
||
### 四、编程题
|
||
|
||
**4-1**:
|
||
|
||
```rust
|
||
enum FsNode {
|
||
File { name: String, size: u64 },
|
||
Directory { name: String, children: Vec<FsNode> },
|
||
}
|
||
|
||
impl FsNode {
|
||
fn name(&self) -> &str {
|
||
match self {
|
||
FsNode::File { name, .. } => name,
|
||
FsNode::Directory { name, .. } => name,
|
||
}
|
||
}
|
||
|
||
fn total_size(&self) -> u64 {
|
||
match self {
|
||
FsNode::File { size, .. } => *size,
|
||
FsNode::Directory { children, .. } => {
|
||
children.iter().map(|c| c.total_size()).sum()
|
||
}
|
||
}
|
||
}
|
||
|
||
fn print_tree(&self, depth: usize) {
|
||
let indent = " ".repeat(depth);
|
||
match self {
|
||
FsNode::File { name, size } => {
|
||
println!("{}{} ({} 字节)", indent, name, size);
|
||
}
|
||
FsNode::Directory { name, children } => {
|
||
println!("{}{} (目录)", indent, name);
|
||
for child in children {
|
||
child.print_tree(depth + 1);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
fn file_count(&self) -> usize {
|
||
match self {
|
||
FsNode::File { .. } => 1,
|
||
FsNode::Directory { children, .. } => {
|
||
children.iter().map(|c| c.file_count()).sum()
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**4-2**:
|
||
|
||
```rust
|
||
enum Operation {
|
||
Add(i32, i32),
|
||
Subtract(i32, i32),
|
||
Multiply(i32, i32),
|
||
Divide(i32, i32),
|
||
}
|
||
|
||
impl Operation {
|
||
fn execute(&self) -> Result<f64, String> {
|
||
match self {
|
||
Operation::Add(a, b) => Ok((a + b) as f64),
|
||
Operation::Subtract(a, b) => Ok((a - b) as f64),
|
||
Operation::Multiply(a, b) => Ok((a * b) as f64),
|
||
Operation::Divide(a, b) => {
|
||
if *b == 0 {
|
||
Err(String::from("除数不能为零"))
|
||
} else {
|
||
Ok(*a as f64 / *b as f64)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
fn symbol(&self) -> &str {
|
||
match self {
|
||
Operation::Add(..) => "+",
|
||
Operation::Subtract(..) => "-",
|
||
Operation::Multiply(..) => "*",
|
||
Operation::Divide(..) => "/",
|
||
}
|
||
}
|
||
|
||
fn describe(&self) -> String {
|
||
match self {
|
||
Operation::Add(a, b) => format!("{} + {}", a, b),
|
||
Operation::Subtract(a, b) => format!("{} - {}", a, b),
|
||
Operation::Multiply(a, b) => format!("{} * {}", a, b),
|
||
Operation::Divide(a, b) => format!("{} / {}", a, b),
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**4-3**:
|
||
|
||
```rust
|
||
enum JsonValue {
|
||
Null,
|
||
Bool(bool),
|
||
Number(f64),
|
||
String(String),
|
||
Array(Vec<JsonValue>),
|
||
Object(Vec<(String, JsonValue)>),
|
||
}
|
||
|
||
impl JsonValue {
|
||
fn type_name(&self) -> &str {
|
||
match self {
|
||
JsonValue::Null => "Null",
|
||
JsonValue::Bool(_) => "Bool",
|
||
JsonValue::Number(_) => "Number",
|
||
JsonValue::String(_) => "String",
|
||
JsonValue::Array(_) => "Array",
|
||
JsonValue::Object(_) => "Object",
|
||
}
|
||
}
|
||
|
||
fn to_string(&self) -> String {
|
||
match self {
|
||
JsonValue::Null => String::from("null"),
|
||
JsonValue::Bool(b) => b.to_string(),
|
||
JsonValue::Number(n) => {
|
||
if *n == n.trunc() {
|
||
format!("{}", *n as i64)
|
||
} else {
|
||
n.to_string()
|
||
}
|
||
}
|
||
JsonValue::String(s) => format!("\"{}\"", s),
|
||
JsonValue::Array(arr) => {
|
||
let items: Vec<String> = arr.iter().map(|v| v.to_string()).collect();
|
||
format!("[{}]", items.join(", "))
|
||
}
|
||
JsonValue::Object(pairs) => {
|
||
let items: Vec<String> = pairs
|
||
.iter()
|
||
.map(|(k, v)| format!(" {}: {}", k, v.to_string()))
|
||
.collect();
|
||
format!("{{\n{}\n}}", items.join(",\n"))
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**4-4**:
|
||
|
||
```rust
|
||
enum OrderState {
|
||
Pending,
|
||
Paid { amount: f64 },
|
||
Shipped { tracking_id: String },
|
||
Delivered,
|
||
Cancelled { reason: String },
|
||
}
|
||
|
||
impl OrderState {
|
||
fn can_cancel(&self) -> bool {
|
||
matches!(self, OrderState::Pending | OrderState::Paid { .. })
|
||
}
|
||
|
||
fn pay(self, amount: f64) -> Result<OrderState, String> {
|
||
match self {
|
||
OrderState::Pending => Ok(OrderState::Paid { amount }),
|
||
_ => Err(format!("当前状态 {} 不允许支付", self.describe())),
|
||
}
|
||
}
|
||
|
||
fn ship(self, tracking_id: String) -> Result<OrderState, String> {
|
||
match self {
|
||
OrderState::Paid { .. } => Ok(OrderState::Shipped { tracking_id }),
|
||
_ => Err(format!("当前状态 {} 不允许发货", self.describe())),
|
||
}
|
||
}
|
||
|
||
fn deliver(self) -> Result<OrderState, String> {
|
||
match self {
|
||
OrderState::Shipped { .. } => Ok(OrderState::Delivered),
|
||
_ => Err(format!("当前状态 {} 不允许签收", self.describe())),
|
||
}
|
||
}
|
||
|
||
fn cancel(self, reason: String) -> Result<OrderState, String> {
|
||
match self {
|
||
OrderState::Pending | OrderState::Paid { .. } => {
|
||
Ok(OrderState::Cancelled { reason })
|
||
}
|
||
_ => Err(format!("当前状态 {} 不允许取消", self.describe())),
|
||
}
|
||
}
|
||
|
||
fn describe(&self) -> String {
|
||
match self {
|
||
OrderState::Pending => String::from("待支付"),
|
||
OrderState::Paid { amount } => format!("已支付 {} 元", amount),
|
||
OrderState::Shipped { tracking_id } => format!("已发货 ({})", tracking_id),
|
||
OrderState::Delivered => String::from("已签收"),
|
||
OrderState::Cancelled { reason } => format!("已取消 ({})", reason),
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**4-5**:
|
||
|
||
```rust
|
||
enum Expr {
|
||
Number(f64),
|
||
Add(Box<Expr>, Box<Expr>),
|
||
Subtract(Box<Expr>, Box<Expr>),
|
||
Multiply(Box<Expr>, Box<Expr>),
|
||
Divide(Box<Expr>, Box<Expr>),
|
||
Negate(Box<Expr>),
|
||
}
|
||
|
||
impl Expr {
|
||
fn eval(&self) -> Result<f64, String> {
|
||
match self {
|
||
Expr::Number(n) => Ok(*n),
|
||
Expr::Add(a, b) => Ok(a.eval()? + b.eval()?),
|
||
Expr::Subtract(a, b) => Ok(a.eval()? - b.eval()?),
|
||
Expr::Multiply(a, b) => Ok(a.eval()? * b.eval()?),
|
||
Expr::Divide(a, b) => {
|
||
let divisor = b.eval()?;
|
||
if divisor == 0.0 {
|
||
Err(String::from("除数不能为零"))
|
||
} else {
|
||
Ok(a.eval()? / divisor)
|
||
}
|
||
}
|
||
Expr::Negate(a) => Ok(-a.eval()?),
|
||
}
|
||
}
|
||
|
||
fn to_string(&self) -> String {
|
||
match self {
|
||
Expr::Number(n) => n.to_string(),
|
||
Expr::Add(a, b) => format!("({} + {})", a.to_string(), b.to_string()),
|
||
Expr::Subtract(a, b) => format!("({} - {})", a.to_string(), b.to_string()),
|
||
Expr::Multiply(a, b) => format!("({} * {})", a.to_string(), b.to_string()),
|
||
Expr::Divide(a, b) => format!("({} / {})", a.to_string(), b.to_string()),
|
||
Expr::Negate(a) => format!("(-{})", a.to_string()),
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
### 五、综合思考题
|
||
|
||
**5-1**:
|
||
|
||
- `some_u8_value` 为 `3u8`,匹配到 `2 | 3 | 4 =>` 分支,输出 "two, three, or four"。
|
||
- `|` 模式(or pattern):适用场景是多个离散值共享同一段处理逻辑,如 `1 | 2 | 3`。
|
||
- `..=` 范围模式(range pattern):适用场景是连续的数值范围,如 `1..=100`。只能在 `char` 和数值类型上使用。
|
||
|
||
**5-2**:
|
||
|
||
- 场景 A:使用 `match`。需要穷尽所有分支并针对每种情况返回值,`match` 最清晰且编译器会检查穷尽性。
|
||
- 场景 B:使用 `if let`。
|
||
```rust
|
||
if let Some(val) = opt {
|
||
println!("{}", val);
|
||
}
|
||
```
|
||
- 场景 C:使用 `matches!` 宏(单次布尔判断最简洁)。
|
||
```rust
|
||
if matches!(x, Some(n) if n > 3) {
|
||
println!("满足条件");
|
||
}
|
||
```
|
||
- 选型总结:
|
||
- `match`:需要穷尽所有模式,或处理多种不同模式时
|
||
- `if let`:只关心一种匹配,其余情况忽略
|
||
- `matches!`:只需要一个布尔判断结果,不需要提取内部值
|
||
|
||
**5-3**:
|
||
|
||
1. 适用场景:
|
||
- `Option<T>`:值**可能不存在**,但不存在不是"错误"。例如:查找元素可能找不到、配置项可能未设置。
|
||
- `Result<T, E>`:操作**可能失败**,失败时需要知道原因。例如:文件读取、网络请求、解析数据等 I/O 操作。
|
||
2. `Option<T>` 转 `Result<T, E>`:
|
||
```rust
|
||
let opt = Some(42);
|
||
let result = opt.ok_or("值不存在");
|
||
// result: Result<i32, &str> = Ok(42)
|
||
|
||
let opt: Option<i32> = None;
|
||
let result = opt.ok_or("值不存在");
|
||
// result: Result<i32, &str> = Err("值不存在")
|
||
```
|
||
3. `Result<T, E>` 转 `Option<T>`:
|
||
```rust
|
||
let res: Result<i32, &str> = Ok(42);
|
||
let opt = res.ok();
|
||
// opt: Option<i32> = Some(42)
|
||
|
||
let res: Result<i32, &str> = Err("出错");
|
||
let opt = res.ok();
|
||
// opt: Option<i32> = None(丢弃错误信息)
|
||
```
|
||
|
||
**5-4**:
|
||
|
||
- 第一次 `match` 时:
|
||
- `Message::Write(s)` 分支将 `msg` 中的 `String` 绑定到 `s`,**值被移动**
|
||
- `Message::Quit` 分支没有内部数据,不会移动
|
||
- 无论匹配到哪个分支,`match` 会获取所有权
|
||
- 取消注释 `println!("{:?}", msg)` 后:编译错误,因为 `msg` 的所有权已在第一次 match 中被消费(特别是当匹配到 Write 分支时 String 被移出)。
|
||
- **Rust 需要 match 的所有权机制**:因为 match 分支可能需要将枚举内部的数据绑定到变量并移动出来。如果允许 match 后继续使用,枚举内部可能已被部分移动。解决方案是使用 `match &msg` 借用来只做模式匹配而不移动所有权。
|
||
- 正确写法(不使用所有权):
|
||
```rust
|
||
match &msg {
|
||
Message::Write(s) => println!("写入: {}", s),
|
||
Message::Quit => println!("退出"),
|
||
}
|
||
// msg 仍然可用
|
||
```
|
||
|
||
**5-5**:
|
||
|
||
1. "没有中间名"的表示:
|
||
- 设计 A:空字符串 `""`(特殊值表示"无")
|
||
- 设计 B:`None`
|
||
2. 设计 B 更符合 Rust 惯用风格。原因:
|
||
- 类型系统明确区分"有值"和"无值",编译器强制检查,避免空字符串被误用为有效数据
|
||
- 避免了"特殊值"的含义歧义(`""` 到底是"没有中间名"还是"中间名叫空字符串"?)
|
||
- 调用方必须显式处理 `None` 情况,减少空值错误
|
||
3. 区分"未提供"和"明确为空"的方案——定义自定义枚举:
|
||
```rust
|
||
enum Maybe<T> {
|
||
NotProvided, // 未提供
|
||
Empty, // 明确为空
|
||
Value(T), // 有值
|
||
}
|
||
|
||
struct User {
|
||
name: String,
|
||
middle_name: Maybe<String>,
|
||
}
|
||
```
|
||
这样在语义上可以区分三种状态:未提供(表单没填这个字段)、明确为空(用户选了"我没有中间名")、有值。
|
||
|
||
</details>
|