# Rust 结构体(Struct)练习题 > 建议先手动写出每道题的答案,再运行代码验证。 ## 目录 - [一、基础题:判断正误](#一基础题判断正误) - [二、填空题:补充代码](#二填空题补充代码) - [三、找出并修复错误](#三找出并修复错误) - [四、编程题](#四编程题) - [五、综合思考题](#五综合思考题) - [参考答案](#参考答案) --- ## 一、基础题:判断正误 判断以下代码能否通过编译。如果可以,说明原因;如果不能,指出错误原因。 ### 题目 1-1 ```rust struct User { username: String, email: String, active: bool, } fn main() { let user1 = User { email: String::from("alice@example.com"), username: String::from("alice"), active: true, }; println!("{}", user1.username); } ``` > 答: 可以通过编译,结构体变量类型都正确,使用方式正确 ### 题目 1-2 ```rust struct Point(f64, f64); fn main() { let p = Point(3.0, 4.0); println!("x = {}, y = {}", p.0, p.1); } ``` > 答: 可以通过编译,结构体变量类型都正确,使用方式正确 ### 题目 1-3 ```rust struct User { username: String, email: String, active: bool, } fn main() { let mut user1 = User { email: String::from("alice@example.com"), username: String::from("alice"), active: true, }; user1.email = String::from("new_email@example.com"); println!("{}", user1.email); } ``` > 答: 可以通过编译,定义了一个可变结构体,可变结构体变量可以修改 ### 题目 1-4 ```rust struct User { username: String, email: String, active: bool, } fn main() { let user1 = User { email: String::from("alice@example.com"), username: String::from("alice"), active: true, }; user1.email = String::from("new@example.com"); // user1 是不可变的 } ``` > 答: 不可以通过编译,定义的`user1`是不可变的,不能修改 ### 题目 1-5 ```rust struct User { username: String, email: String, active: bool, } fn main() { let user1 = User { email: String::from("alice@example.com"), username: String::from("alice"), active: true, }; let user2 = User { email: String::from("bob@example.com"), ..user1 }; println!("user1: {}", user1.username); println!("user2: {}", user2.username); } ``` > **[批注] 回答正确但原因有误。** "user1 是不可变的"与所有权转移无关——即使 `user1` 是 `mut`,`..user1` 同样会移动非 Copy 字段。真正原因:结构体更新语法 `..user1` 会将 `user1` 中未显式赋值的非 Copy 字段(`username`)移动到 `user2`,导致 `user1` 整体无法再使用。 ### 题目 1-6 ```rust struct User { username: String, email: String, active: bool, } fn main() { let user1 = User { email: String::from("alice@example.com"), username: String::from("alice"), active: true, }; let user2 = User { email: String::from("bob@example.com"), ..user1 }; println!("user1 email: {}", user1.email); println!("user2 email: {}", user2.email); } ``` > 答: 可以通过编译,没有再次使用`user1.username` ### 题目 1-7 ```rust #[derive(Debug)] struct Rectangle { width: u32, height: u32, } fn main() { let rect = Rectangle { width: 30, height: 50, }; println!("{:?}", rect); } ``` > 答: 可以通过编译,使用`#[derive(Debug)]`宏,打印结构体变量 ### 题目 1-8 ```rust struct Color(i32, i32, i32); fn main() { let black = Color(0, 0, 0); let white = Color(255, 255, 255); println!("black R: {}, white R: {}", black.0, white.0); } ``` > 答: 可以通过编译,定义和使用方式都正确 --- ## 二、填空题:补充代码 补全下列代码使其能通过编译并达到期望输出。 ### 题目 2-1:定义结构体并实例化 ```rust // 填空:定义一个名为 Book 的结构体,包含 title(String)、author(String)、pages(u32) fn main() { let book = ________ { title: String::from("深入浅出 Rust"), ________: String::from("张三"), pages: 320, }; println!("《{}》作者:{},共 {} 页", book.title, book.author, book.pages); // 期望输出:《深入浅出 Rust》作者:张三,共 320 页 } ``` 答: ```rust struct Book { title: String, author: String, pages: u32, } fn main() { let book = Book { title: String::from("深入浅出 Rust"), author: String::from("张三"), pages: 320, }; println!( "《{}》作者:{},共 {} 页", book.title, book.author, book.pages ); } ``` ### 题目 2-2:元组结构体 ```rust // 填空:定义一个元组结构体 RGB,包含三个 u8 值(红、绿、蓝) struct RGB(________, ________, ________); fn main() { let red = RGB(255, 0, 0); let green = RGB(0, 255, 0); println!("红色: ({}, {}, {})", red.0, red.1, ________); println!("绿色: ({}, {}, {})", green.0, green.1, green.2); } ``` 答: ```rust struct RGB(u8, u8, u8); fn main() { let red = RGB(255, 0, 0); let green = RGB(0, 255, 0); println!("红色: ({}, {}, {})", red.0, red.1, red.2); println!("绿色: ({}, {}, {})", green.0, green.1, green.2); } ``` ### 题目 2-3:结构体更新语法 ```rust #[derive(Debug)] struct Student { name: String, age: u32, score: u32, } fn main() { let s1 = Student { name: String::from("小明"), age: 18, score: 85, }; let s2 = Student { name: String::from("小红"), ________ // 填空:使用结构体更新语法,其余字段从 s1 复制 }; println!("s1: {:?}, s2: {:?}", s1, s2); // 注意:s1.score 和 s1.age 在更新后是否还能使用?为什么? } ``` 答: ```rust #[derive(Debug)] struct Student { name: String, age: u32, score: u32, } fn main() { let s1 = Student { name: String::from("小明"), age: 18, score: 85, }; let s2 = Student { name: String::from("小红"), ..s1 // 填空:使用结构体更新语法,其余字段从 s1 复制 }; println!("s1: {:?}, s2: {:?}", s1, s2); // 注意:s1.score 和 s1.age 在更新后是否还能使用?为什么? // s1.score 和 s1.age 在更新时所有权没有转移,所以仍然可以使用 } ``` ### 题目 2-4:定义方法 ```rust struct Rectangle { width: u32, height: u32, } impl Rectangle { // 填空:定义一个 area 方法,计算矩形的面积 fn ________(&self) -> ________ { self.width ________ self.height } // 填空:定义一个 can_hold 方法,判断当前矩形能否容纳另一个矩形 fn can_hold(&self, other: ________) -> bool { self.width > other.width && self.height > other.height } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; let rect2 = Rectangle { width: 10, height: 40 }; let rect3 = Rectangle { width: 60, height: 45 }; println!("rect1 面积: {}", rect1.area()); // 期望输出:1500 println!("rect1 能容纳 rect2: {}", rect1.can_hold(&rect2)); // 期望输出:true println!("rect1 能容纳 rect3: {}", rect1.can_hold(&rect3)); // 期望输出:false } ``` 答: ```rust struct Rectangle { width: u32, height: u32, } impl Rectangle { // 填空:定义一个 area 方法,计算矩形的面积 fn area(&self) -> u32 { self.width * self.height } // 填空:定义一个 can_hold 方法,判断当前矩形能否容纳另一个矩形 fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height } } fn main() { let rect1 = Rectangle { width: 30, height: 50 }; let rect2 = Rectangle { width: 10, height: 40 }; let rect3 = Rectangle { width: 60, height: 45 }; println!("rect1 面积: {}", rect1.area()); // 期望输出:1500 println!("rect1 能容纳 rect2: {}", rect1.can_hold(&rect2)); // 期望输出:true println!("rect1 能容纳 rect3: {}", rect1.can_hold(&rect3)); // 期望输出:false } ``` ### 题目 2-5:关联函数 ```rust struct Rectangle { width: u32, height: u32, } impl Rectangle { // 填空:定义一个关联函数 square,接收一个边长参数,返回一个正方形 Rectangle fn ________(size: u32) -> ________ { Rectangle { width: size, height: ________, } } } fn main() { let square = Rectangle::square(10); println!("正方形: {} x {}", square.width, square.height); // 期望输出:正方形: 10 x 10 } ``` 答: ```rust struct Rectangle { width: u32, height: u32, } impl Rectangle { // 填空:定义一个关联函数 square,接收一个边长参数,返回一个正方形 Rectangle fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size, } } } fn main() { let square = Rectangle::square(10); println!("正方形: {} x {}", square.width, square.height); // 期望输出:正方形: 10 x 10 } ``` ### 题目 2-6:self / &self / &mut self ```rust struct Counter { count: u32, } impl Counter { // A:创建新的 Counter,初始值为 0 fn new() -> Counter { Counter { count: 0 } } // B:只读取计数值 —— 参数应该是什么? fn value(________) -> u32 { self.count } // C:计数 +1 —— 参数应该是什么? fn increment(________) { self.count += 1; } // D:消费 Counter,返回最终值 —— 参数应该是什么? fn into_value(________) -> u32 { self.count } } fn main() { let mut counter = Counter::new(); counter.increment(); counter.increment(); println!("当前值: {}", counter.value()); // 期望输出:2 println!("最终值: {}", counter.into_value()); // 期望输出:2 // println!("{}", counter.value()); // 如果取消注释会怎样? } ``` 答: ```rust struct Counter { count: u32, } impl Counter { // A:创建新的 Counter,初始值为 0 fn new() -> Counter { Counter { count: 0 } } // B:只读取计数值 —— 参数应该是什么? fn value(&self) -> u32 { self.count } // C:计数 +1 —— 参数应该是什么? fn increment(&mut self) { self.count += 1; } // D:消费 Counter,返回最终值 —— 参数应该是什么? fn into_value(self) -> u32 { self.count } } fn main() { let mut counter = Counter::new(); counter.increment(); counter.increment(); println!("当前值: {}", counter.value()); // 期望输出:2 println!("最终值: {}", counter.into_value()); // 期望输出:2 // println!("{}", counter.value()); // 如果取消注释会怎样? } ``` --- ## 三、找出并修复错误 以下每段代码都有编译错误,请指出错误并写出修正后的代码。 ### 题目 3-1 ```rust struct User { username: &str, email: &str, active: bool, } fn main() { let user = User { username: "alice", email: "alice@example.com", active: true, }; println!("{}", user.username); } ``` ```rust > **[批注] 原分析的诊断不准确。** "定义结构体不能使用引用类型"是错误的——Rust 完全允许结构体包含引用字段,但必须标注**生命周期参数(lifetime)**。原代码 `username: &str` 缺少生命周期标注 `'a`。两种修复: > 1. 加生命周期:`struct User<'a> { username: &'a str, email: &'a str, active: bool }` > 2. 改用 String(无需生命周期,推荐初学者使用) struct User { username: String, email: String, active: bool, } fn main() { let user = User { username: String::from("alice"), email: String::from("alice@example.com"), active: true, }; println!("{}", user.username); } ``` ### 题目 3-2 ```rust #[derive(Debug)] struct Point { x: i32, y: i32, } fn main() { let p = Point { x: 10, y: 20 }; println!("{}", p); // 想要打印 p } ``` ```rust // 编译错误,打印要使用{:?} #[derive(Debug)] struct Point { x: i32, y: i32, } fn main() { let p = Point { x: 10, y: 20 }; println!("{:?}", p); // 想要打印 p } ``` ### 题目 3-3 ```rust struct Rectangle { width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect = Rectangle { width: 30, height: 50 }; let a = rect::area(); // 尝试调用方法 println!("面积: {}", a); } ``` ```rust // 错误,方法调用需要使用. struct Rectangle { width: u32, height: u32, } impl Rectangle { fn area(&self) -> u32 { self.width * self.height } } fn main() { let rect = Rectangle { width: 30, height: 50 }; let a = rect.area(); // 尝试调用方法 println!("面积: {}", a); } ``` ### 题目 3-4 ```rust struct User { name: String, age: u32, } impl User { fn greet(self) { println!("你好,我是 {},今年 {} 岁", self.name, self.age); } } fn main() { let user = User { name: String::from("小明"), age: 18, }; user.greet(); user.greet(); // 第二次调用 } ``` ```rust // 错误,第二次调用user.greet()之前,user的所有权已转移 struct User { name: String, age: u32, } impl User { fn greet(&self) { println!("你好,我是 {},今年 {} 岁", self.name, self.age); } } fn main() { let user = User { name: String::from("小明"), age: 18, }; user.greet(); user.greet(); // 第二次调用 } ``` ### 题目 3-5 ```rust struct Container { value: i32, } impl Container { fn get_value(&self) -> &i32 { &self.value } fn set_value(&mut self, new_value: i32) { self.value = new_value; } } fn main() { let mut c = Container { value: 10 }; let v = c.get_value(); c.set_value(20); println!("v = {}", v); } ``` ```rust // 错误,调用c.set_value(20)之前存在一个不可变引用,不能同时存在不可变引用和可变引用,因此将println提前,使得v的作用域提前结束 struct Container { value: i32, } impl Container { fn get_value(&self) -> &i32 { &self.value } fn set_value(&mut self, new_value: i32) { self.value = new_value; } } fn main() { let mut c = Container { value: 10 }; let v = c.get_value(); println!("v = {}", v); c.set_value(20); } ``` --- ## 四、编程题 ### 题目 4-1:学生管理系统基础结构 定义一个 `Student` 结构体,包含 `name`(String)、`id`(u32)、`grades`(Vec\),并实现以下方法: ```rust struct Student { name: String, id: u32, grades: Vec, } impl Student { // 1. 关联函数 new:创建一个新学生(成绩列表为空) fn new(name: String, id: u32) -> Student { // 你的代码 } // 2. 添加一门成绩 fn add_grade(&mut self, grade: u32) { // 你的代码 } // 3. 计算平均成绩,如果没有成绩则返回 0.0 fn average_grade(&self) -> f64 { // 你的代码 } // 4. 判断是否及格(平均分 >= 60) fn is_passing(&self) -> bool { // 你的代码 } } fn main() { let mut student = Student::new(String::from("小明"), 1001); student.add_grade(85); student.add_grade(92); student.add_grade(78); println!("{} 的平均分: {:.1}", student.name, student.average_grade()); // 期望:85.0 println!("{} 是否及格: {}", student.name, student.is_passing()); // 期望:true let student2 = Student::new(String::from("小红"), 1002); student2.add_grade(55); student2.add_grade(58); println!("{} 是否及格: {}", student2.name, student2.is_passing()); // 期望:false } ``` ```rust struct Student { name: String, id: u32, grades: Vec, } impl Student { // 1. 关联函数 new:创建一个新学生(成绩列表为空) fn new(name: String, id: u32) -> Student { Student { name, id, grades: Vec::new(), } } // 2. 添加一门成绩 fn add_grade(&mut self, grade: u32) { self.grades.push(grade); } // 3. 计算平均成绩,如果没有成绩则返回 0.0 fn average_grade(&self) -> f64 { if self.grades.is_empty() { return 0.0; } self.grades.iter().sum::() as f64 / self.grades.len() as f64 } // 4. 判断是否及格(平均分 >= 60) fn is_passing(&self) -> bool { self.average_grade() >= 60.0 } } fn main() { let mut student = Student::new(String::from("小明"), 1001); student.add_grade(85); student.add_grade(92); student.add_grade(78); println!("{} 的平均分: {:.1}", student.name, student.average_grade()); // 期望:85.0 println!("{} 是否及格: {}", student.name, student.is_passing()); // 期望:true let mut student2 = Student::new(String::from("小红"), 1002); student2.add_grade(55); student2.add_grade(58); println!("{} 是否及格: {}", student2.name, student2.is_passing()); // 期望:false } ``` ### 题目 4-2:几何图形 定义一个 `Circle` 结构体和 `Rectangle` 结构体,为它们分别实现 `area` 和 `perimeter` 方法。 ```rust use std::f64::consts::PI; struct Circle { radius: f64, } struct Rectangle { width: f64, height: f64, } // 你的代码:为 Circle 实现 area 和 perimeter // 你的代码:为 Rectangle 实现 area 和 perimeter fn main() { let circle = Circle { radius: 5.0 }; println!("圆面积: {:.2}, 圆周长: {:.2}", circle.area(), circle.perimeter()); // 期望:圆面积: 78.54, 圆周长: 31.42 let rect = Rectangle { width: 3.0, height: 4.0 }; println!("矩形面积: {:.2}, 矩形周长: {:.2}", rect.area(), rect.perimeter()); // 期望:矩形面积: 12.00, 矩形周长: 14.00 } ``` ```rust use std::f64::consts::PI; struct Circle { radius: f64, } struct Rectangle { width: f64, height: f64, } // 为 Circle 实现 area 和 perimeter impl Circle { fn area(&self) -> f64 { PI * self.radius * self.radius } fn perimeter(&self) -> f64 { 2.0 * PI * self.radius } } // 为 Rectangle 实现 area 和 perimeter impl Rectangle { fn area(&self) -> f64 { self.width * self.height } fn perimeter(&self) -> f64 { 2.0 * (self.width + self.height) } } fn main() { let circle = Circle { radius: 5.0 }; println!( "圆面积: {:.2}, 圆周长: {:.2}", circle.area(), circle.perimeter() ); // 期望:圆面积: 78.54, 圆周长: 31.42 let rect = Rectangle { width: 3.0, height: 4.0, }; println!( "矩形面积: {:.2}, 矩形周长: {:.2}", rect.area(), rect.perimeter() ); // 期望:矩形面积: 12.00, 矩形周长: 14.00 } ``` ### 题目 4-3:温度转换器 使用元组结构体实现温度类型,使不同类型的温度之间不会混淆。 ```rust // 定义三个元组结构体:Celsius(f64)、Fahrenheit(f64)、Kelvin(f64) // 你的代码 impl Celsius { // 转换为华氏度 fn to_fahrenheit(&self) -> Fahrenheit { // °F = °C × 9/5 + 32 } // 转换为开尔文 fn to_kelvin(&self) -> Kelvin { // K = °C + 273.15 } } impl Fahrenheit { // 转换为摄氏度 fn to_celsius(&self) -> Celsius { // °C = (°F - 32) × 5/9 } } fn main() { let temp_c = Celsius(100.0); let temp_f = temp_c.to_fahrenheit(); let temp_k = temp_c.to_kelvin(); println!("{}°C = {}°F = {}K", temp_c.0, temp_f.0, temp_k.0); // 期望:100°C = 212°F = 373.15K let temp_f2 = Fahrenheit(32.0); let temp_c2 = temp_f2.to_celsius(); println!("{}°F = {}°C", temp_f2.0, temp_c2.0); // 期望:32°F = 0°C } ``` ```rust use std::ops::Mul; // 定义三个元组结构体:Celsius(f64)、Fahrenheit(f64)、Kelvin(f64) struct Celsius(f64); struct Fahrenheit(f64); struct Kelvin(f64); // 你的代码 impl Celsius { // 转换为华氏度 fn to_fahrenheit(&self) -> Fahrenheit { // °F = °C × 9/5 + 32 Fahrenheit(self.0 * 9.0 / 5.0 + 32.0) } // 转换为开尔文 fn to_kelvin(&self) -> Kelvin { // K = °C + 273.15 Kelvin(self.0 + 273.15) } } impl Fahrenheit { // 转换为摄氏度 fn to_celsius(&self) -> Celsius { // °C = (°F - 32) × 5/9 Celsius((self.0 - 32.0).mul(5.0 / 9.0)) } } fn main() { let temp_c = Celsius(100.0); let temp_f = temp_c.to_fahrenheit(); let temp_k = temp_c.to_kelvin(); println!("{}°C = {}°F = {}K", temp_c.0, temp_f.0, temp_k.0); // 期望:100°C = 212°F = 373.15K let temp_f2 = Fahrenheit(32.0); let temp_c2 = temp_f2.to_celsius(); println!("{}°F = {}°C", temp_f2.0, temp_c2.0); // 期望:32°F = 0°C } ``` ### 题目 4-4:链表节点 使用结构体实现一个单向链表的节点,要求展示你对 `Box` 和所有权机制的理解。 ```rust // 定义一个 ListNode 结构体,包含 value(i32) 和 next(Option>) // 你的代码 impl ListNode { // 创建一个新节点 fn new(value: i32) -> ListNode { // 你的代码 } // 在链表末尾追加一个节点(递归实现) fn append(&mut self, value: i32) { // 你的代码 } // 将所有节点的值收集到一个 Vec 中 fn collect(&self) -> Vec { // 你的代码 } } fn main() { let mut head = ListNode::new(1); head.append(2); head.append(3); let values = head.collect(); println!("链表元素: {:?}", values); // 期望:链表元素: [1, 2, 3] } ``` ```rust // 定义一个 ListNode 结构体,包含 value(i32) 和 next(Option>) struct ListNode { value: i32, next: Option>, } // 你的代码 impl ListNode { // 创建一个新节点 fn new(value: i32) -> ListNode { ListNode { value, next: Option::None, } } // 在链表末尾追加一个节点(递归实现) fn append(&mut self, value: i32) { match self.next { Some(ref mut next_node) => next_node.append(value), None => self.next = Some(Box::new(ListNode::new(value))), } } // 将所有节点的值收集到一个 Vec 中 fn collect(&self) -> Vec { let mut values = vec![self.value]; let mut current = &self.next; while let Some(node) = current { values.push(node.value); current = &node.next; } values } } fn main() { let mut head = ListNode::new(1); head.append(2); head.append(3); let values = head.collect(); println!("链表元素: {:?}", values); // 期望:链表元素: [1, 2, 3] } ``` > **[批注] 原 `append` 和 `collect` 实现有严重错误,已在上方代码中修正:** > - `append` 原实现 `self.next = Some(...)` 每次都覆盖 next,第二次 append 会丢掉第一次加的值。正确做法是用 `match self.next` 递归到尾节点再插入。 > - `collect` 原实现只 push 了 `self.value` 一个值,遗漏后续所有节点。正确做法是遍历链表。 ### 题目 4-5:购物车 定义一个 `ShoppingCart` 结构体,包含商品列表(Vec),设计合理的所有权和借用关系。 ```rust #[derive(Debug, Clone)] struct Item { name: String, price: f64, quantity: u32, } impl Item { fn new(name: &str, price: f64, quantity: u32) -> Item { // 你的代码 } // 计算该商品总价(单价 × 数量) fn total(&self) -> f64 { // 你的代码 } } struct ShoppingCart { items: Vec, } impl ShoppingCart { // 创建空购物车 fn new() -> ShoppingCart { // 你的代码 } // 添加商品 fn add_item(&mut self, item: Item) { // 你的代码 } // 计算购物车中所有商品的总价 fn total(&self) -> f64 { // 你的代码 } // 获取商品种类数量 fn item_count(&self) -> usize { // 你的代码 } // 打印购物车明细(消耗购物车所有权) fn print_receipt(self) { // 你的代码:遍历所有商品,打印名称、单价、数量、小计 // 最后打印总价 } } fn main() { let mut cart = ShoppingCart::new(); cart.add_item(Item::new("键盘", 299.0, 1)); cart.add_item(Item::new("鼠标", 159.0, 2)); cart.add_item(Item::new("显示器", 1899.0, 1)); println!("商品种类: {}", cart.item_count()); // 期望:3 println!("总价: {:.2}", cart.total()); // 期望:2516.00 cart.print_receipt(); // 期望输出: // === 购物明细 === // 键盘 × 1 @ 299.00 = 299.00 // 鼠标 × 2 @ 159.00 = 318.00 // 显示器 × 1 @ 1899.00 = 1899.00 // ---------------------- // 总计: 2516.00 // ====================== // println!("{:?}", cart.item_count()); // 如果取消注释会怎样?为什么? } ``` ```rust #[derive(Debug, Clone)] struct Item { name: String, price: f64, quantity: u32, } impl Item { fn new(name: &str, price: f64, quantity: u32) -> Item { Item { name: name.to_string(), price, quantity, } } // 计算该商品总价(单价 × 数量) fn total(&self) -> f64 { self.price * self.quantity as f64 } } struct ShoppingCart { items: Vec, } impl ShoppingCart { // 创建空购物车 fn new() -> ShoppingCart { ShoppingCart { items: Vec::new() } } // 添加商品 fn add_item(&mut self, item: Item) { self.items.push(item); } // 计算购物车中所有商品的总价 fn total(&self) -> f64 { self.items.iter().map(|item| item.total()).sum::() } // 获取商品种类数量 fn item_count(&self) -> usize { self.items.len() } // [批注] 原实现了 fn print_receipt(&self),但题目要求 fn print_receipt(self) 消耗所有权 fn print_receipt(self) { // 你的代码:遍历所有商品,打印名称、单价、数量、小计 // 最后打印总价 println!("=== 购物明细 ==="); for item in self.items.iter() { println!( "{} × {} @ {:.2} = {:.2}", item.name, item.quantity, item.price, item.total() ); } println!("----------------------"); println!("总价: {:.2}", self.total()); } } fn main() { let mut cart = ShoppingCart::new(); cart.add_item(Item::new("键盘", 299.0, 1)); cart.add_item(Item::new("鼠标", 159.0, 2)); cart.add_item(Item::new("显示器", 1899.0, 1)); println!("商品种类: {}", cart.item_count()); // 期望:3 println!("总价: {:.2}", cart.total()); // 期望:2516.00 cart.print_receipt(); // println!("{:?}", cart.item_count()); // 如果取消注释会怎样?为什么? } ``` --- ## 五、综合思考题 ### 题目 5-1 以下代码为什么能通过编译?`user1` 的 `active` 字段是 `bool` 类型(实现 Copy),而 `username` 和 `email` 是 `String` 类型(未实现 Copy),使用结构体更新语法后,各自的可用性如何? ```rust struct User { username: String, email: String, active: bool, } fn main() { let user1 = User { email: String::from("alice@example.com"), username: String::from("alice"), active: true, }; let user2 = User { email: String::from("bob@example.com"), ..user1 }; // 下面哪些行可以取消注释?为什么? // println!("{}", user1.username); // println!("{}", user1.email); // println!("{}", user1.active); } ``` > 答: > // println!("{}", user1.email); > // println!("{}", user1.active); > 可以取消注释,使用结构体更新语法后,user1的username失效,email和active有效 ### 题目 5-2 对比以下三种设计。在什么场景下分别应该使用哪种? ```rust // 设计 A:所有字段都是 String(自有类型) struct UserA { name: String, email: String, } // 设计 B:所有字段都是 &str(借用) struct UserB<'a> { name: &'a str, email: &'a str, } // 设计 C:混合 struct UserC { name: String, email: String, cached_display: Option, // 缓存 } ``` > **[批注] 原答太简略,只描述了"是什么"没回答"什么时候用"。** > - **设计 A(全部 String)**:结构体拥有数据,适合需要独立生命周期、长期持有数据的场景(如从函数返回、存入集合)。额外 heap 分配开销。 > - **设计 B(全部 `&'a str`)**:仅仅借用外部数据,zero-copy,但受限于引用的生命周期 `'a`,结构体不能比它借用的数据活得更久。适合临时视图、解析器的 AST 节点等。 > - **设计 C(混合 + 缓存)**:String 拥有核心数据,`cached_display` 作为惰性计算的缓存。适合需要性能优化但保留所有权的场景。 ### 题目 5-3 分析以下代码,回答: 1. `self`、`&self`、`&mut self` 三者在调用时的区别是什么? > 答:self:方法接收一个结构体的所有权,方法调用结束后,结构体将不再可用;&self:方法接收结构体的引用,方法调用结束后,结构体仍然可用;&mut self:方法接收结构体的可变引用,方法调用结束后,结构体仍然可用,并且允许修改结构体的字段。 2. 当调用完 `into_value()` 之后,`counter` 是否还能使用?为什么? > **[批注] 基本正确,但表述可更精确:** `into_value()` 的参数是 `self`(不是引用),调用时 `counter` 的所有权被**移入**方法,方法结束 `self` 被 drop,返回的 `u32` 是 Copy 类型独立于原结构体。"返回所有权"的说法容易混淆——方法不是返回所有权,而是**接收**所有权。 ```rust struct Counter { count: u32, } impl Counter { fn value(&self) -> u32 { self.count } fn increment(&mut self) { self.count += 1; } fn reset(&mut self) { self.count = 0; } fn into_value(self) -> u32 { self.count } } fn main() { let mut counter = Counter { count: 0 }; counter.increment(); counter.increment(); println!("value: {}", counter.value()); let n = counter.into_value(); println!("final: {}", n); } ``` ### 题目 5-4 下面的代码涉及 struct、impl 和模块的组合。有两个 struct 都定义了 `area` 方法,Rust 通过什么机制区分它们? > **[批注] 原答错误。** "通过 self 参数区分"没有回答编译器如何做方法分派。正确答案:Rust 根据**方法接收者的类型**分派——`c.area()` 中 `c` 的类型是 `Circle`,编译器调用 `Circle::area`;`r.area()` 中 `r` 的类型是 `Rectangle`,编译器调用 `Rectangle::area`。`impl Circle` 和 `impl Rectangle` 是两个独立的 impl 块,各自的方法只属于各自的类型,不会混淆。这是编译期静态分派,与 self 无关。 ```rust struct Circle { radius: f64, } struct Rectangle { width: f64, height: f64, } impl Circle { fn area(&self) -> f64 { std::f64::consts::PI * self.radius * self.radius } } impl Rectangle { fn area(&self) -> f64 { self.width * self.height } } fn main() { let c = Circle { radius: 3.0 }; let r = Rectangle { width: 4.0, height: 5.0 }; println!("圆面积: {:.2}", c.area()); // Circle::area(&c) println!("矩形面积: {:.2}", r.area()); // Rectangle::area(&r) } ``` ### 题目 5-5 元组结构体 `struct Point(i32, i32)` 和普通元组 `(i32, i32)` 有什么区别?在什么场景下应该使用元组结构体而不是普通元组? > **[批注] 原答有多处错误。** > 1. "元组结构体的字段顺序不要求与定义一致,使用命名即可" — **错误**,元组结构体没有命名字段,只能用索引 `.0` `.1` 访问。这是与普通结构体的核心区别。 > 2. "元组结构体的字段是可变的,普通元组字段是只读的" — **错误**,可变性与类型无关,取决于 `let mut`。 > > **正确区分**: > - 元组结构体 `struct Point(i32, i32)` 是一个**具名的新类型**,与 `struct Vector(i32, i32)` 是不同型别,不能互相赋值,提供类型安全。可以在其上 `impl` 方法和 trait。 > - 普通元组 `(i32, i32)` 是匿名类型,任何 `(i32, i32)` 都可互相赋值,没有类型区分能力。 --- ## 参考答案 > 请独立完成再查看答案。
点击展开答案 ### 一、基础题 **1-1**:✅ 通过编译。结构体中的字段顺序不要求与定义一致,使用命名即可。 **1-2**:✅ 通过编译。元组结构体使用 `.索引` 方式访问字段。 **1-3**:✅ 通过编译。`mut` 修饰的结构体实例,其所有字段都是可变的。 **1-4**:❌ 编译错误。`user1` 不是 `mut` 的,不能修改其字段。Rust 中可变性是整个结构体级别的,不能只标记某个字段为 `mut`。 **1-5**:❌ 编译错误。结构体更新语法 `..user1` 会移动 `user1` 中未实现 Copy 的字段(`username` 和 `email`)。`user1.username` 已被移动到 `user2`,`user1` 不再可用。 **1-6**:✅ 通过编译。`..user1` 展开时,显式赋值的 `email` 不会从 `user1` 移动,只有未显式赋值的非 Copy 字段(`username`)会被移动。`user1.email` 仍然有效,`user1.active`(Copy)也有效。但 `user1` 整体已部分移动,不能整体使用。 > **[批注] 此答案修正了原隐藏答案的错误(原答说 ❌ 编译错误)。** 结构体更新语法在显式覆盖字段时不会移动该字段。但若 `user1.username` 已移动,`user1` 作为一个整体不能再传递给其他函数。 **1-7**:✅ 通过编译。`#[derive(Debug)]` 自动为结构体实现了 `Debug` trait,允许使用 `{:?}` 格式化打印。 **1-8**:✅ 通过编译。元组结构体的实例各自独立,互不影响。 ### 二、填空题 **2-1**: ```rust struct Book { title: String, author: String, pages: u32, } let book = Book { title: String::from("深入浅出 Rust"), author: String::from("张三"), pages: 320, }; ``` **2-2**: ```rust struct RGB(u8, u8, u8); println!("红色: ({}, {}, {})", red.0, red.1, red.2); ``` **2-3**: ```rust let s2 = Student { name: String::from("小红"), ..s1 }; // s1.score 和 s1.age 在更新后是否还能使用? // — 如果字段类型是 Copy 的(u32),可以继续使用 // — 如果字段类型不是 Copy 的(String),已被移动,不能使用 // 这里 age 和 score 都是 u32(Copy),所以 s1.score 和 s1.age 仍然可用 ``` **2-4**: ```rust fn area(&self) -> u32 { self.width * self.height } fn can_hold(&self, other: &Rectangle) -> bool { self.width > other.width && self.height > other.height } ``` **2-5**: ```rust fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size, } } ``` **2-6**: ```rust fn value(&self) -> u32 { self.count } // 不可变借用 fn increment(&mut self) { self.count += 1; } // 可变借用 fn into_value(self) -> u32 { self.count } // 获取所有权 // 最后一行取消注释会报错:into_value() 消费了 counter,之后不能再使用 ``` ### 三、修复错误 **3-1**:结构体使用了引用字段但没有标注生命周期。修复: ```rust struct User<'a> { username: &'a str, email: &'a str, active: bool, } ``` 或者改用 `String` 类型(推荐给初学者): ```rust struct User { username: String, email: String, active: bool, } ``` **3-2**:`println!("{}", p)` 需要 `Display` trait,但只 derive 了 `Debug`。修复: ```rust println!("{:?}", p); // 使用 Debug 格式化 // 或 println!("{}", p) 行不通 → 需改为 println!("{:?}", p) ``` **3-3**:方法调用语法错误。`rect::area()` 应改为 `rect.area()`。`::` 用于关联函数,`.` 用于方法调用。 **3-4**:`greet(self)` 获取了所有权,第一次调用后 `user` 被消费。修复: ```rust fn greet(&self) { // 改为不可变引用 println!("你好,我是 {},今年 {} 岁", self.name, self.age); } ``` **3-5**:`get_value` 的不可变借用和 `set_value` 的可变借用存在生命周期重叠。修复:让 `v` 在 `c.set_value` 之前结束使用: ```rust fn main() { let mut c = Container { value: 10 }; let v = c.get_value(); println!("v = {}", v); // 先使用 v c.set_value(20); // v 不再使用,可以创建可变借用 } ``` ### 四、编程题 **4-1**: ```rust fn new(name: String, id: u32) -> Student { Student { name, id, grades: Vec::new() } } fn add_grade(&mut self, grade: u32) { self.grades.push(grade); } fn average_grade(&self) -> f64 { if self.grades.is_empty() { 0.0 } else { self.grades.iter().sum::() as f64 / self.grades.len() as f64 } } fn is_passing(&self) -> bool { self.average_grade() >= 60.0 } ``` **4-2**: ```rust impl Circle { fn area(&self) -> f64 { PI * self.radius * self.radius } fn perimeter(&self) -> f64 { 2.0 * PI * self.radius } } impl Rectangle { fn area(&self) -> f64 { self.width * self.height } fn perimeter(&self) -> f64 { 2.0 * (self.width + self.height) } } ``` **4-3**: ```rust struct Celsius(f64); struct Fahrenheit(f64); struct Kelvin(f64); impl Celsius { fn to_fahrenheit(&self) -> Fahrenheit { Fahrenheit(self.0 * 9.0 / 5.0 + 32.0) } fn to_kelvin(&self) -> Kelvin { Kelvin(self.0 + 273.15) } } impl Fahrenheit { fn to_celsius(&self) -> Celsius { Celsius((self.0 - 32.0) * 5.0 / 9.0) } } ``` **4-4**: ```rust struct ListNode { value: i32, next: Option>, } impl ListNode { fn new(value: i32) -> ListNode { ListNode { value, next: None } } fn append(&mut self, value: i32) { match &mut self.next { Some(next_node) => next_node.append(value), None => self.next = Some(Box::new(ListNode::new(value))), } } fn collect(&self) -> Vec { let mut result = vec![self.value]; let mut current = &self.next; while let Some(node) = current { result.push(node.value); current = &node.next; } result } } ``` **4-5**: ```rust impl Item { fn new(name: &str, price: f64, quantity: u32) -> Item { Item { name: String::from(name), price, quantity } } fn total(&self) -> f64 { self.price * self.quantity as f64 } } impl ShoppingCart { fn new() -> ShoppingCart { ShoppingCart { items: Vec::new() } } fn add_item(&mut self, item: Item) { self.items.push(item); } fn total(&self) -> f64 { self.items.iter().map(|item| item.total()).sum() } fn item_count(&self) -> usize { self.items.len() } fn print_receipt(self) { println!("=== 购物明细 ==="); for item in &self.items { println!("{} × {} @ {:.2} = {:.2}", item.name, item.quantity, item.price, item.total()); } println!("----------------------"); println!("总计: {:.2}", self.total()); println!("======================"); } } // print_receipt(self) 消耗了购物车所有权,之后再调用 item_count() 会报错 ``` ### 五、综合思考题 **5-1**: - `user1.username`(String)— 已被 `..user1` 移动到 `user2`,**不可用** - `user1.email`(String)— 在 `user2` 中被显式赋予新值,**未被移动,可用** - `user1.active`(bool,Copy 类型)— 被复制,**可用** > **[批注] 此答案修正了原隐藏答案关于 user1.email 的错误。** 显式覆盖的字段不会从原结构体移动。 **5-2**: - **设计 A**:适合需要拥有数据的场景,如从函数返回新结构体、需要独立生命周期、长期持有的数据 - **设计 B**:适合临时借用、只读引用外部数据、需要避免堆分配的场景;缺点是结构体受限于引用的生命周期 - **设计 C**:适合需要高性能 + 灵活性的场景,但增加了维护缓存的复杂性 **5-3**: - `&self`:不可变借用,只读,可多次调用,调用后实例仍可用 - `&mut self`:可变借用,可修改,同时只能有一个,调用后实例仍可用 - `self`:获取所有权,可修改或消费,调用后实例被 drop,不可再用 - `into_value()` 获取了 `counter` 的所有权,调用后 `counter` 不再可用 **5-4**:Rust 通过**方法调用的接收者类型**来区分。`c.area()` 的接收者是 `Circle`,调用 `Circle::area`;`r.area()` 的接收者是 `Rectangle`,调用 `Rectangle::area`。 **5-5**: - 元组结构体 `Point(i32, i32)` 是一个**命名类型**,与 `Vec2(i32, i32)` 是不同的类型,不能互相赋值,提供了类型安全 - 普通元组 `(i32, i32)` 是匿名类型,任何 `(i32, i32)` 都可以互相赋值 - 使用元组结构体的场景:需要类型安全地区分语义不同的同构数据(如 `Celsius(f64)` vs `Fahrenheit(f64)`),或为特定类型实现方法和 trait