# Rust Vector 练习题 > 建议先手动写出每道题的答案,再运行代码验证。 ## 目录 - [一、概念辨析题](#一概念辨析题) - [二、填空题:补充代码](#二填空题补充代码) - [三、找出并修复错误](#三找出并修复错误) - [四、编程题](#四编程题) - [五、综合思考题](#五综合思考题) - [参考答案](#参考答案) --- ## 一、概念辨析题 判断以下说法是否正确,并简要说明原因。 ### 题目 1-1 > `Vec` 只能存储相同类型的数据。 ### 题目 1-2 > 从空 Vec 中 `pop()` 会引发 panic。 ### 题目 1-3 > 以下两种创建 Vec 的方式完全等价,没有性能差异: > ```rust > let v1 = Vec::new(); > let v2 = vec![]; > ``` ### 题目 1-4 > 使用索引 `v[3]` 访问 Vec 元素和使用 `v.get(3)` 的行为完全一致。 ### 题目 1-5 > 对 Vec 进行 `for` 循环遍历会消耗 Vec 的所有权,遍历后 Vec 不再可用。 ### 题目 1-6 > 当 Vec 的容量满了再 `push` 新元素时,Rust 会自动重新分配一块更大的内存并将原数据拷贝过去。 ### 题目 1-7 > `Vec::with_capacity(n)` 创建的 Vec 的长度也为 `n`。 ### 题目 1-8 > 可以在遍历 Vec 的同时向其中添加元素。 --- ## 二、填空题:补充代码 补全下列代码使其能通过编译并达到期望输出。 ### 题目 2-1:基本创建和操作 ```rust fn main() { // 填空:创建一个包含元素 1, 2, 3, 4, 5 的 Vec let mut v = ________; // 填空:向末尾添加元素 6 v.________(6); // 填空:移除并返回最后一个元素 let last = v.________(); println!("移除的元素是: {}", last); // 填空:获取当前长度 println!("当前长度: {}", ________); // 填空:获取指定位置元素的值(安全方式) match v.________(2) { Some(value) => println!("索引 2 的值是: {}", value), None => println!("没有该元素"), } } // 期望输出: // 移除的元素是: 6 // 当前长度: 5 // 索引 2 的值是: 3 ``` ### 题目 2-2:遍历 Vec ```rust fn main() { let v = vec![10, 20, 30, 40, 50]; // 填空:不可变遍历,打印每个元素 for ________ in ________ { print!("{} ", i); } println!(); // 填空:可变遍历,每个元素翻倍 let mut v2 = vec![1, 2, 3, 4, 5]; for ________ in ________ { *i ________ 2; } println!("翻倍后: {:?}", v2); } // 期望输出: // 10 20 30 40 50 // 翻倍后: [2, 4, 6, 8, 10] ``` ### 题目 2-3:插入和删除 ```rust fn main() { let mut v = vec![1, 2, 4, 5]; // 填空:在索引 2 处插入元素 3 v.________(2, 3); // 填空:删除索引 4 处的元素 v.________(4); // 填空:删除索引 0 处的元素 v.________(0); println!("结果: {:?}", v); } // 期望输出:结果: [2, 3, 4] ``` ### 题目 2-4:枚举实现不同类型存储 ```rust // 填空:定义一个枚举,包含整数、浮点数、文本三种变体 ________ Value { Integer(i32), Float(f64), ________(String), } fn main() { // 填空:使用宏创建一个包含不同变体的 Vec let row = ________; Value::Integer(3), Value::Float(3.14), Value::Text(String::from("Hello")), ]; for v in &row { match v { Value::Integer(n) => print!("Int:{} ", n), Value::Float(f) => print!("Float:{} ", f), Value::Text(s) => print!("Text:{} ", s), } } } // 期望输出:Int:3 Float:3.14 Text:Hello ``` ### 题目 2-5:切片操作 ```rust fn main() { let v = vec![1, 2, 3, 4, 5, 6, 7, 8]; // 填空:获取 v[1..4] 的切片 let slice1 = ________; println!("slice1: {:?}", slice1); // 填空:获取前 3 个元素的切片 let slice2 = ________; println!("slice2: {:?}", slice2); // 填空:获取从索引 5 到末尾的切片 let slice3 = ________; println!("slice3: {:?}", slice3); } // 期望输出: // slice1: [2, 3, 4] // slice2: [1, 2, 3] // slice3: [6, 7, 8] ``` ### 题目 2-6:容量管理 ```rust fn main() { // 填空:预分配容量为 100 的空 Vec let mut v: Vec = Vec::________(100); println!("创建后 - 长度: {},容量: {}", v.len(), v.capacity()); for i in 0..50 { v.push(i); } println!("添加 50 个元素后 - 长度: {},容量: {}", v.len(), v.capacity()); // 填空:收缩容量以匹配长度(使用合适的方法) v.________(); println!("收缩后 - 长度: {},容量: {}", v.len(), v.capacity()); } ``` ### 题目 2-7:常用方法组合 ```rust fn main() { let mut v = vec![5, 2, 8, 1, 9, 3]; // 填空:排序(升序) v.________(); // 填空:检查是否包含某个值 let has_five = v.________(&5); // 填空:反转顺序 v.________(); println!("排序并反转后: {:?}", v); println!("是否包含 5: {}", has_five); } // 期望输出: // 排序并反转后: [9, 8, 5, 3, 2, 1] // 是否包含 5: true ``` ### 题目 2-8:extend 和 drain ```rust fn main() { let mut v1 = vec![1, 2, 3]; let v2 = vec![4, 5, 6]; // 填空:将 v2 的所有元素追加到 v1 v1.________(&v2); println!("追加后: {:?}", v1); let mut v = vec![10, 20, 30, 40, 50]; // 填空:移除索引 1..4 的元素,返回被移除元素的迭代器 let drained: Vec<_> = v.________(1..4).collect(); println!("被移除的: {:?}", drained); println!("剩余的: {:?}", v); } // 期望输出: // 追加后: [1, 2, 3, 4, 5, 6] // 被移除的: [20, 30, 40] // 剩余的: [10, 50] ``` ### 题目 2-9:迭代器适配器 ```rust fn main() { let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; // 填空:过滤出偶数,映射为平方,收集到新 Vec let result: Vec = v.________() .________(|x| x % 2 == 0) .________(|x| x * x) .collect(); println!("偶数的平方: {:?}", result); // 填空:求和 let sum: i32 = v.iter().________(); println!("总和: {}", sum); } // 期望输出: // 偶数的平方: [4, 16, 36, 64, 100] // 总和: 55 ``` --- ## 三、找出并修复错误 以下每段代码都有编译错误,请指出错误并写出修正后的代码。 ### 题目 3-1 ```rust fn main() { let mut v = vec![1, 2, 3]; let first = &v[0]; // 不可变借用 v.push(4); // 可变借用 println!("{}", first); // 使用不可变借用 } ``` ### 题目 3-2 ```rust fn main() { let v = vec![1, 2, 3, 4, 5]; let third = v[10]; println!("{}", third); } ``` ### 题目 3-3 ```rust fn main() { let mut v = Vec::new(); v.push("hello"); v.push(42); println!("{:?}", v); } ``` ### 题目 3-4 ```rust fn main() { let mut v = vec![1, 2, 3]; for i in v.iter_mut() { v.push(4); // 同时修改 } println!("{:?}", v); } ``` ### 题目 3-5 ```rust fn main() { let mut v = vec![1, 2, 3, 4, 5]; for i in 0..v.len() { if v[i] % 2 == 0 { v.remove(i); } } println!("{:?}", v); } ``` ### 题目 3-6 ```rust fn main() { let v = vec![String::from("hello")]; let s = &v[0]; // 借用了 v 中的元素 drop(v); // 显式释放 v println!("{}", s); // 使用了 s } ``` ### 题目 3-7 ```rust fn main() { let v = vec![1, 2, 3]; // 想要获得 [1, 4, 9] let squared: Vec = v.iter().map(|x| x * x); println!("{:?}", squared); } ``` ### 题目 3-8 ```rust fn main() { let mut v: Vec = Vec::new(); v.push(1); let popped = v.pop(); println!("{:?}", popped); // 期望打印 1 let popped2 = v.pop(); // Vec 已空 println!("{}", popped2); // 这行会怎样? } ``` ### 题目 3-9 ```rust fn main() { let v = vec![1, 2, 3, 4, 5]; for item in v { println!("{}", item); } println!("v 还有 {} 个元素", v.len()); // 再次使用 v } ``` ### 题目 3-10 ```rust fn main() { let mut v = vec![1, 2, 3]; let slice = &mut v[0..2]; // 可变借用切片 v.push(4); // 尝试修改原 Vec slice[0] = 10; println!("{:?}", v); } ``` --- ## 四、编程题 请在 `main.rs` 中编写以下程序,并实际运行验证。 ### 题目 4-1:成绩统计分析器 编写一个程序,完成以下功能: 1. 创建一个包含 10 个学生成绩的 `Vec`(成绩范围 0~100) 2. 计算并打印:最高分、最低分、平均分 3. 找出所有不及格(< 60)的成绩,存入新 Vec 并打印 4. 判断是否有满分(100 分),打印结果 5. 将所有成绩从高到低排序后打印 **期望输出示例(成绩不同则输出不同):** ``` 成绩列表: [85, 42, 96, 78, 55, 88, 91, 63, 100, 74] 最高分: 100 最低分: 42 平均分: 77.2 不及格成绩: [42, 55] 有满分: 是 排序后(高到低): [100, 96, 91, 88, 85, 78, 74, 63, 55, 42] ``` ### 题目 4-2:斐波那契数列生成器 编写一个函数 `fibonacci(n: usize) -> Vec`,接收项数 `n`,返回一个包含前 `n` 项斐波那契数列的 Vec。 - n = 0 时返回空 Vec - n = 1 时返回 `[1]` - n = 2 时返回 `[1, 1]` - n >= 3 时正常生成 在 `main` 中调用并打印前 20 项。 **期望输出(前 20 项):** ``` [1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233, 377, 610, 987, 1597, 2584, 4181, 6765] ``` ### 题目 4-3:购物车模拟 编写一个模拟购物车的程序: 1. 定义一个 `Item` 结构体,包含 `name: String` 和 `price: f64` 2. 创建一个购物车 `Vec`,提供以下功能: - `add_item(cart: &mut Vec, name: &str, price: f64)` — 添加商品 - `remove_item(cart: &mut Vec, name: &str)` — 按名称移除商品(只移除第一个匹配项) - `total_price(cart: &Vec) -> f64` — 计算总价 - `list_items(cart: &Vec)` — 列出所有商品 3. 在 `main` 中演示完整流程:添加 5 个商品,列出清单,计算总价,移除一个商品,再次列出清单和计算总价 **期望输出示例:** ``` 购物车清单: - 苹果: ¥3.50 - 香蕉: ¥2.00 - 牛奶: ¥6.80 - 面包: ¥5.00 - 鸡蛋: ¥12.00 当前总价: ¥29.30 移除"香蕉"后: 购物车清单: - 苹果: ¥3.50 - 牛奶: ¥6.80 - 面包: ¥5.00 - 鸡蛋: ¥12.00 当前总价: ¥27.30 ``` ### 题目 4-4:单词统计器 编写一个程序,统计一段文本中每个单词出现的次数。 1. 定义输入文本:`"the quick brown fox jumps over the lazy dog the fox was quick"` 2. 将文本按空格分割成单词,存入 `Vec<&str>` 3. 使用一个辅助的 Vec(或使用 `HashMap`,但核心流程用 Vec 实现)统计每个不重复单词及其出现次数 4. 打印所有不重复单词及其出现次数(按单词字母顺序排序) **提示:** - 可以用 `Vec<(String, i32)>` 存储"单词-次数"对 - 遍历单词 Vec,如果在统计 Vec 中找到该单词则计数+1,否则追加新纪录 **期望输出:** ``` 单词统计结果: brown: 1 dog: 1 fox: 2 jumps: 1 lazy: 1 over: 1 quick: 2 the: 3 was: 1 ``` ### 题目 4-5:矩阵转置 使用 `Vec>` 表示一个二维矩阵,编写一个函数 `transpose(matrix: &Vec>) -> Vec>` 实现矩阵转置。 - 输入:一个 m×n 的矩阵 - 输出:一个 n×m 的转置矩阵 - 使用迭代器方法实现,避免使用显式的 C 风格循环 在 `main` 中测试: ``` 原始矩阵: [1, 2, 3] [4, 5, 6] [7, 8, 9] 转置后: [1, 4, 7] [2, 5, 8] [3, 6, 9] ``` ### 题目 4-6:简易栈(Stack)实现 只使用 `Vec` 实现一个 `Stack` 结构体,封装栈操作: - `fn new() -> Self` - `fn push(&mut self, item: T)` - `fn pop(&mut self) -> Option` - `fn peek(&self) -> Option<&T>` - `fn is_empty(&self) -> bool` - `fn len(&self) -> usize` 在 `main` 中测试括号匹配功能:编写函数 `is_balanced(s: &str) -> bool`,利用你实现的 `Stack` 判断字符串中的 `()`、`[]`、`{}` 是否正确匹配。 测试用例: ``` 测试用例: "()" -> true "()[]{}" -> true "(]" -> false "([)]" -> false "{[]}" -> true "(((" -> false "" -> true ``` --- ## 五、综合思考题 ### 题目 5-1 阅读以下代码: ```rust fn main() { let mut v = vec![String::from("Rust"), String::from("is"), String::from("awesome")]; let first = &v[0]; // ① v.push(String::from("!")); // ② println!("{}", first); // ③ } ``` 1. 这段代码能编译吗?为什么? 2. 如果改成 `let first = &v[0].clone();`,会怎样?为什么? 3. `v[0]` 和 `v.get(0)` 的区别在哪里?分别适用于什么场景? 4. 如果 `v` 的类型是 `Vec`,问题 1 的代码是否能编译?为什么? ### 题目 5-2 分析以下两种删除 Vec 中所有偶数元素的方式: **方式 A:** ```rust fn remove_evens_a(v: &mut Vec) { let mut i = 0; while i < v.len() { if v[i] % 2 == 0 { v.remove(i); } else { i += 1; } } } ``` **方式 B:** ```rust fn remove_evens_b(v: &mut Vec) { v.retain(|x| x % 2 != 0); } ``` 1. 两种方式的时间复杂度分别是多少?为什么? 2. 方式 A 中的 `i += 1` 放在 `else` 分支中,如果去掉这个条件(每次循环都 `i += 1`)会发生什么? 3. `retain` 方法的底层原理是什么?它比手动 `remove` 高效在哪里? ### 题目 5-3 ```rust fn first_n_even(n: usize) -> Vec { let mut result = Vec::new(); let mut num = 2; while result.len() < n { if num % 2 == 0 { result.push(num); } num += 1; } result } ``` 1. 上述代码有没有更好的 Rust 风格写法?请使用迭代器重写。 2. 如果 `n` 非常大(比如 10000000),从性能角度应该如何优化?(提示:`Vec::with_capacity` 和迭代器) ### 题目 5-4 阅读以下代码,回答: ```rust fn append_vector(v: &mut Vec, other: &mut Vec) { v.append(other); } fn main() { let mut v1 = vec![1, 2, 3]; let mut v2 = vec![4, 5, 6]; append_vector(&mut v1, &mut v2); println!("v1: {:?}", v1); println!("v2: {:?}", v2); } ``` 1. `v2` 在 `append` 调用后会变成什么?为什么? 2. `append` 和 `extend` 有什么区别?各适用于什么场景? 3. 如果希望 `v2` 在追加后保持不变(即"复制追加"),应该使用什么方法? ### 题目 5-5 假设你正在实现一个游戏中的物品栏系统: ```rust #[derive(Debug)] struct Inventory { items: Vec, capacity: usize, } #[derive(Debug, Clone)] struct Item { name: String, count: u32, } impl Inventory { fn new(capacity: usize) -> Self { Inventory { items: Vec::new(), capacity, } } fn add_item(&mut self, item: Item) -> Result<(), String> { // 待实现 todo!() } fn remove_item(&mut self, name: &str, count: u32) -> Result<(), String> { // 待实现 todo!() } } ``` 1. 实现 `add_item`:如果物品已存在则叠加数量;如果背包已满则返回错误信息。注意借用检查器的约束。 2. 实现 `remove_item`:如果数量足够则减少,如果降至 0 则移除整个条目;如果物品不存在或数量不足则返回错误信息。 3. 这个设计中有一个潜在的问题:当背包容量已满但要添加的是已有物品时,叠加数量并不需要新增槽位。如何修改 `add_item` 的逻辑来正确处理这种情况? --- ## 参考答案 > 请独立完成再查看答案。
点击展开答案 ### 一、概念辨析题 **1-1**:✅ 正确。`Vec` 是泛型容器,所有元素必须是相同类型 `T`。如果要存储不同类型,需要使用枚举(如 `Vec`)或 trait 对象(`Vec>`)。 **1-2**:❌ 错误。空 Vec 调用 `pop()` 不会 panic,而是返回 `None`。`pop` 的返回类型是 `Option`,在空 Vec 上返回 `None`。 **1-3**:❌ 错误。`Vec::new()` 创建一个容量为 0 的 Vec(不分配堆内存),`vec![]` 同样不分配。两者在这种情况下等价。但如果使用 `vec![1, 2, 3]` 和手动 `new` 后 `push` 三次,`vec![]` 可能预先分配合适的容量,性能略好。 **1-4**:❌ 错误。`v[3]` 索引越界时会 **panic**(程序崩溃),而 `v.get(3)` 返回 `Option<&T>`,越界时返回 `None`,不会崩溃。`get` 是安全的访问方式。 **1-5**:❌ 错误。`for item in v` 会消耗 Vec 的所有权,但 `for item in &v` 仅不可变借用,`for item in &mut v` 仅可变借用。只有转移所有权的遍历才会使 Vec 之后不可用。 **1-6**:✅ 正确。当 `len == capacity` 时再 push,Vec 会自动分配一块更大的内存(通常是当前容量的两倍),将旧元素移动/拷贝到新内存,然后释放旧内存。 **1-7**:❌ 错误。`Vec::with_capacity(n)` 创建的 Vec **长度**为 0,**容量**为 n。需要通过 `push` 等方法添加元素后长度才会增加。 **1-8**:❌ 错误。在遍历 Vec 时不能同时修改 Vec 本身(添加或删除元素),这违反了借用规则。遍历时持有的是对 Vec 或其元素的借用,而修改需要可变借用,两者不能共存。 ### 二、填空题 **2-1**: ```rust let mut v = vec![1, 2, 3, 4, 5]; v.push(6); let last = v.pop().unwrap(); println!("当前长度: {}", v.len()); match v.get(2) { ``` **2-2**: ```rust for i in &v { print!("{} ", i); } // 可变遍历 for i in &mut v2 { *i *= 2; } ``` **2-3**: ```rust v.insert(2, 3); v.remove(4); v.remove(0); ``` **2-4**: ```rust enum Value { Integer(i32), Float(f64), Text(String), } let row = vec![ Value::Integer(3), Value::Float(3.14), Value::Text(String::from("Hello")), ]; ``` **2-5**: ```rust let slice1 = &v[1..4]; let slice2 = &v[..3]; let slice3 = &v[5..]; ``` **2-6**: ```rust let mut v: Vec = Vec::with_capacity(100); // ... 添加元素后 v.shrink_to_fit(); ``` **2-7**: ```rust v.sort(); let has_five = v.contains(&5); v.reverse(); ``` **2-8**: ```rust v1.extend(&v2); let drained: Vec<_> = v.drain(1..4).collect(); ``` **2-9**: ```rust let result: Vec = v.iter() .filter(|x| *x % 2 == 0) .map(|x| x * x) .collect(); let sum: i32 = v.iter().sum(); ``` ### 三、修复错误 **3-1**:不可变借用和可变借用冲突。`first` 是 `v` 的不可变引用,而 `push` 需要 `v` 的可变引用。修复:调整顺序,先使用 `first` 再 `push`,或在 push 前不保留引用。 ```rust let mut v = vec![1, 2, 3]; let first = v[0]; // 直接拷贝值(i32 实现了 Copy) v.push(4); println!("{}", first); ``` 对于非 Copy 类型,可以: ```rust let first = v[0].clone(); // 克隆值 v.push(4); println!("{}", first); ``` **3-2**:索引越界,Vec 只有 5 个元素(索引 0~4),访问 `v[10]` 会 panic。修复: ```rust let third = v.get(10); // 返回 None,不会 panic match third { Some(val) => println!("{}", val), None => println!("索引越界"), } ``` **3-3**:`Vec::new()` 无法推断类型,需要显式标注。且同一个 Vec 不能混装 `&str` 和 `i32`。修复:使用枚举: ```rust enum Value<'a> { Text(&'a str), Number(i32), } let mut v: Vec = Vec::new(); v.push(Value::Text("hello")); v.push(Value::Number(42)); ``` **3-4**:在遍历 Vec 的同时修改 Vec 的大小,违反借用规则。修复:先收集要添加的元素,再 `extend`: ```rust let mut v = vec![1, 2, 3]; v.extend(vec![4, 4, 4]); println!("{:?}", v); ``` **3-5**:一边按索引遍历一边删除元素会导致跳过元素(因为删除后后续元素前移)。修复:使用 `retain` 或从后往前遍历: ```rust let mut v = vec![1, 2, 3, 4, 5]; v.retain(|x| x % 2 != 0); println!("{:?}", v); // [1, 3, 5] ``` **3-6**:`s` 借用了 `v` 中的元素,`drop(v)` 释放 `v` 后 `s` 变成悬垂引用。修复:先 `clone` 再释放: ```rust let v = vec![String::from("hello")]; let s = v[0].clone(); // 获取独立的所有权 drop(v); println!("{}", s); ``` **3-7**:`map` 返回迭代器,需要 `.collect()` 消费才能得到 Vec。修复: ```rust let squared: Vec = v.iter().map(|x| x * x).collect(); ``` **3-8**:`pop` 返回 `Option`,不能直接用 `println!("{}", popped2)` 打印(类型不匹配,且值为 `None`)。修复: ```rust println!("{:?}", popped); // Some(1) println!("{:?}", popped2); // None ``` **3-9**:`for item in v` 消耗了 `v` 的所有权,之后不能再用 `v`。修复:使用引用遍历: ```rust for item in &v { println!("{}", item); } println!("v 还有 {} 个元素", v.len()); ``` **3-10**:`slice` 是 `v` 的可变借用,`push` 需要另一个可变借用,冲突。修复:先操作切片,释放后再 push: ```rust let mut v = vec![1, 2, 3]; { let slice = &mut v[0..2]; slice[0] = 10; } // slice 的借用在此结束 v.push(4); println!("{:?}", v); // [10, 2, 3, 4] ``` ### 四、编程题 **4-1 成绩统计分析器:** ```rust fn main() { let scores = vec![85, 42, 96, 78, 55, 88, 91, 63, 100, 74]; println!("成绩列表: {:?}", scores); let max = scores.iter().max().unwrap(); let min = scores.iter().min().unwrap(); let avg = scores.iter().sum::() as f64 / scores.len() as f64; println!("最高分: {}", max); println!("最低分: {}", min); println!("平均分: {:.1}", avg); let failing: Vec = scores.iter().filter(|&&s| s < 60).copied().collect(); println!("不及格成绩: {:?}", failing); let has_perfect = scores.contains(&100); println!("有满分: {}", if has_perfect { "是" } else { "否" }); let mut sorted = scores.clone(); sorted.sort_by(|a, b| b.cmp(a)); println!("排序后(高到低): {:?}", sorted); } ``` **4-2 斐波那契数列生成器:** ```rust fn fibonacci(n: usize) -> Vec { match n { 0 => vec![], 1 => vec![1], _ => { let mut fib = vec![1, 1]; for _ in 2..n { let next = fib[fib.len() - 1] + fib[fib.len() - 2]; fib.push(next); } fib } } } fn main() { let fib = fibonacci(20); println!("{:?}", fib); } ``` **4-3 购物车模拟:** ```rust struct Item { name: String, price: f64, } fn add_item(cart: &mut Vec, name: &str, price: f64) { cart.push(Item { name: name.to_string(), price, }); } fn remove_item(cart: &mut Vec, name: &str) { if let Some(pos) = cart.iter().position(|item| item.name == name) { cart.remove(pos); } } fn total_price(cart: &Vec) -> f64 { cart.iter().map(|item| item.price).sum() } fn list_items(cart: &Vec) { for item in cart { println!(" - {}: ¥{:.2}", item.name, item.price); } } fn main() { let mut cart = Vec::new(); add_item(&mut cart, "苹果", 3.50); add_item(&mut cart, "香蕉", 2.00); add_item(&mut cart, "牛奶", 6.80); add_item(&mut cart, "面包", 5.00); add_item(&mut cart, "鸡蛋", 12.00); println!("购物车清单:"); list_items(&cart); println!("当前总价: ¥{:.2}", total_price(&cart)); remove_item(&mut cart, "香蕉"); println!("\n移除\"香蕉\"后:"); println!("购物车清单:"); list_items(&cart); println!("当前总价: ¥{:.2}", total_price(&cart)); } ``` **4-4 单词统计器:** ```rust fn main() { let text = "the quick brown fox jumps over the lazy dog the fox was quick"; let words: Vec<&str> = text.split_whitespace().collect(); let mut stats: Vec<(String, i32)> = Vec::new(); for word in &words { if let Some(pos) = stats.iter().position(|(w, _)| w == word) { stats[pos].1 += 1; } else { stats.push((word.to_string(), 1)); } } stats.sort_by(|a, b| a.0.cmp(&b.0)); println!("单词统计结果:"); for (word, count) in &stats { println!(" {}: {}", word, count); } } ``` **4-5 矩阵转置:** ```rust fn transpose(matrix: &Vec>) -> Vec> { if matrix.is_empty() { return vec![]; } let rows = matrix.len(); let cols = matrix[0].len(); (0..cols) .map(|col| { (0..rows) .map(|row| matrix[row][col]) .collect() }) .collect() } fn main() { let matrix = vec![ vec![1, 2, 3], vec![4, 5, 6], vec![7, 8, 9], ]; println!("原始矩阵:"); for row in &matrix { println!("{:?}", row); } let transposed = transpose(&matrix); println!("\n转置后:"); for row in &transposed { println!("{:?}", row); } } ``` **4-6 简易栈实现:** ```rust struct Stack { data: Vec, } impl Stack { fn new() -> Self { Stack { data: Vec::new() } } fn push(&mut self, item: T) { self.data.push(item); } fn pop(&mut self) -> Option { self.data.pop() } fn peek(&self) -> Option<&T> { self.data.last() } fn is_empty(&self) -> bool { self.data.is_empty() } fn len(&self) -> usize { self.data.len() } } fn is_balanced(s: &str) -> bool { let mut stack = Stack::new(); for ch in s.chars() { match ch { '(' | '[' | '{' => stack.push(ch), ')' => { if stack.pop() != Some('(') { return false; } } ']' => { if stack.pop() != Some('[') { return false; } } '}' => { if stack.pop() != Some('{') { return false; } } _ => {} } } stack.is_empty() } fn main() { let tests = [ ("()", true), ("()[]{}", true), ("(]", false), ("([)]", false), ("{[]}", true), ("(((", false), ("", true), ]; println!("括号匹配测试:"); for (input, expected) in tests { let result = is_balanced(input); let status = if result == expected { "✓" } else { "✗" }; println!(" {} \"{}\" -> {} (期望: {})", status, input, result, expected); } } ``` ### 五、综合思考题 **5-1**: 1. 不能编译。`first` 是 `v[0]` 的不可变引用,而 `v.push()` 需要可变引用。即便 `push` 触发的内存重分配不影响索引 0 的元素(逻辑上 String 的数据在堆上),但 Rust 的借用规则在编译期不能精确判断这一点,因此直接拒绝在持有不可变引用时进行可变操作。 2. `v[0].clone()` 会创建一个新的 `String`,`first` 拥有这个新 String 的所有权,不再依赖 `v`。因此 `v.push()` 不受影响,可以编译。 3. `v[0]` 返回 `&T`,越界时 panic;`v.get(0)` 返回 `Option<&T>`,越界时返回 `None`。`v[0]` 适用于确定索引有效的场景,`v.get(0)` 适用于索引可能越界的场景。 4. 可以编译。因为 `i32` 实现了 `Copy` trait,`let first = &v[0]` 在后续使用中,由于 `first` 是 `&i32`,`println!` 打印时需要解引用为 `i32` 值。但关键是将 `let first = &v[0]` 改成 `let first = v[0]` 后,`first` 是 `i32` 值(Copy),与 `v` 无关,更安全。使用 `&v[0]` 仍然会有借用冲突。 **5-2**: 1. 方式 A 的时间复杂度是 O(n²),因为每次 `remove(i)` 都需要将后续元素向前移动一位。方式 B 的 `retain` 是 O(n),内部使用一次遍历 + 一次批量移动实现。 2. 如果每次都 `i += 1`,删除偶数元素后索引不会重新检查当前位置(因为后续元素前移了),会**跳过**紧随其后的元素。例如 `[2, 2, 1]`,删除索引 0 的元素 2 后,数组变为 `[2, 1]`,i 变成 1,第二个 2 被跳过。 3. `retain` 的底层原理类似于"双指针":慢指针 `j` 指向下一个要保留的位置,快指针 `i` 遍历所有元素。当元素满足保留条件时,移动到 `j` 位置并使 `j++`;否则跳过。遍历完成后截断到 `j`。避免了每次删除都移动后续元素的 O(n²) 开销。 **5-3**: 1. 迭代器重写: ```rust fn first_n_even(n: usize) -> Vec { (1..).filter(|x| x % 2 == 0).take(n).collect() } ``` 2. 优化: ```rust fn first_n_even_optimized(n: usize) -> Vec { let mut result = Vec::with_capacity(n); // 预分配 (1..).filter(|x| x % 2 == 0).take(n).for_each(|x| result.push(x)); result } // 或者更简单的:从 2 开始每次加 2 fn first_n_even_optimized(n: usize) -> Vec { (2..).step_by(2).take(n).collect() } ``` 使用 `Vec::with_capacity(n)` 预分配可以避免多次重分配的开销。 **5-4**: 1. `v2` 变成空 Vec `[]`。因为 `append` 方法将所有元素**移动**到目标 Vec,并清空源 Vec。这是 O(1) 时间,只交换了指针。 2. 区别: - `append`:移动所有元素,源 Vec 被清空。适用于需要转移所有权的场景。 - `extend`:从迭代器克隆/复制元素到目标 Vec,源数据保持不变。适用于需要保留源的场景。 - 性能上 `append` 只需交换内部指针,是 O(1);`extend` 需要逐个复制,是 O(n)。 3. 使用 `extend` 或 `clone`: ```rust v1.extend_from_slice(&v2); // v2 保持不变 // 或 v1.extend(v2.iter().cloned()); // 迭代器方式 ``` **5-5**: ```rust fn add_item(&mut self, item: Item) -> Result<(), String> { // 先检查是否有同名物品 if let Some(existing) = self.items.iter_mut().find(|i| i.name == item.name) { existing.count += item.count; return Ok(()); } // 新增物品,检查容量 if self.items.len() >= self.capacity { return Err(format!("背包已满({}/{})", self.items.len(), self.capacity)); } self.items.push(item); Ok(()) } fn remove_item(&mut self, name: &str, count: u32) -> Result<(), String> { let pos = self .items .iter() .position(|i| i.name == name) .ok_or(format!("物品「{}」不存在", name))?; if self.items[pos].count < count { return Err(format!( "物品「{}」数量不足(需要 {},当前 {})", name, count, self.items[pos].count )); } self.items[pos].count -= count; if self.items[pos].count == 0 { self.items.remove(pos); } Ok(()) } ``` 关键点是先检查是否可以叠加已有物品(不需要新槽位),再检查容量限制。这样当物品已存在时,即使背包"已满"也能正确叠加数量。