1130 lines
25 KiB
Markdown
1130 lines
25 KiB
Markdown
# 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 是不可变的
|
||
}
|
||
```
|
||
|
||
### 题目 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);
|
||
}
|
||
```
|
||
|
||
### 题目 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);
|
||
}
|
||
```
|
||
|
||
### 题目 1-7
|
||
|
||
```rust
|
||
#[derive(Debug)]
|
||
struct Rectangle {
|
||
width: u32,
|
||
height: u32,
|
||
}
|
||
|
||
fn main() {
|
||
let rect = Rectangle {
|
||
width: 30,
|
||
height: 50,
|
||
};
|
||
println!("{:?}", rect);
|
||
}
|
||
```
|
||
|
||
### 题目 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 页
|
||
}
|
||
```
|
||
|
||
### 题目 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);
|
||
}
|
||
```
|
||
|
||
### 题目 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 在更新后是否还能使用?为什么?
|
||
}
|
||
```
|
||
|
||
### 题目 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
|
||
}
|
||
```
|
||
|
||
### 题目 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
|
||
}
|
||
```
|
||
|
||
### 题目 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()); // 如果取消注释会怎样?
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 三、找出并修复错误
|
||
|
||
以下每段代码都有编译错误,请指出错误并写出修正后的代码。
|
||
|
||
### 题目 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);
|
||
}
|
||
```
|
||
|
||
### 题目 3-2
|
||
|
||
```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);
|
||
}
|
||
```
|
||
|
||
### 题目 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(); // 第二次调用
|
||
}
|
||
```
|
||
|
||
### 题目 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);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 四、编程题
|
||
|
||
### 题目 4-1:学生管理系统基础结构
|
||
|
||
定义一个 `Student` 结构体,包含 `name`(String)、`id`(u32)、`grades`(Vec\<u32\>),并实现以下方法:
|
||
|
||
```rust
|
||
struct Student {
|
||
name: String,
|
||
id: u32,
|
||
grades: Vec<u32>,
|
||
}
|
||
|
||
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
|
||
}
|
||
```
|
||
|
||
### 题目 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
|
||
}
|
||
```
|
||
|
||
### 题目 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
|
||
}
|
||
```
|
||
|
||
### 题目 4-4:链表节点
|
||
|
||
使用结构体实现一个单向链表的节点,要求展示你对 `Box` 和所有权机制的理解。
|
||
|
||
```rust
|
||
// 定义一个 ListNode 结构体,包含 value(i32) 和 next(Option<Box<ListNode>>)
|
||
|
||
// 你的代码
|
||
|
||
impl ListNode {
|
||
// 创建一个新节点
|
||
fn new(value: i32) -> ListNode {
|
||
// 你的代码
|
||
}
|
||
|
||
// 在链表末尾追加一个节点(递归实现)
|
||
fn append(&mut self, value: i32) {
|
||
// 你的代码
|
||
}
|
||
|
||
// 将所有节点的值收集到一个 Vec 中
|
||
fn collect(&self) -> Vec<i32> {
|
||
// 你的代码
|
||
}
|
||
}
|
||
|
||
fn main() {
|
||
let mut head = ListNode::new(1);
|
||
head.append(2);
|
||
head.append(3);
|
||
|
||
let values = head.collect();
|
||
println!("链表元素: {:?}", values);
|
||
// 期望:链表元素: [1, 2, 3]
|
||
}
|
||
```
|
||
|
||
### 题目 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<Item>,
|
||
}
|
||
|
||
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()); // 如果取消注释会怎样?为什么?
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 五、综合思考题
|
||
|
||
### 题目 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);
|
||
}
|
||
```
|
||
|
||
### 题目 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<String>, // 缓存
|
||
}
|
||
```
|
||
|
||
### 题目 5-3
|
||
|
||
分析以下代码,回答:
|
||
1. `self`、`&self`、`&mut self` 三者在调用时的区别是什么?
|
||
2. 当调用完 `into_value()` 之后,`counter` 是否还能使用?为什么?
|
||
|
||
```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 通过什么机制区分它们?
|
||
|
||
```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)` 有什么区别?在什么场景下应该使用元组结构体而不是普通元组?
|
||
|
||
---
|
||
|
||
## 参考答案
|
||
|
||
> 请独立完成再查看答案。
|
||
|
||
<details>
|
||
<summary>点击展开答案</summary>
|
||
|
||
### 一、基础题
|
||
|
||
**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` 移动了 `user1` 的 `username` 字段,而 `email` 字段被显式赋值所以没有移动。但 `user1` 作为一个整体已部分移动,不能再访问 `user1.email`。
|
||
|
||
**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::<u32>() 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<Box<ListNode>>,
|
||
}
|
||
|
||
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<i32> {
|
||
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)— 已被移动给 user2,不可用
|
||
- `user1.email`(String)— 被显式赋予了新值,没有被移动,**可用**
|
||
- `user1.active`(bool,Copy 类型)— 被复制,**可用**
|
||
|
||
关键点:结构体更新语法 `..user1` 相当于 `username: user1.username`、`active: user1.active` 等。对于 String 类型是移动,对于 bool 是复制。显式赋值的字段不会从 user1 移动。
|
||
|
||
**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
|
||
|
||
</details>
|