From 739ebd50475b8c4c07b9dda0aa2edddb2fc5a0bc Mon Sep 17 00:00:00 2001 From: sparksfly Date: Sun, 12 Jul 2026 21:22:28 +0800 Subject: [PATCH] =?UTF-8?q?chore:=20=E6=96=B0=E5=A2=9E=E4=BA=86part4?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E4=B8=8B=E7=9A=84Rust=E5=9F=BA=E7=A1=80?= =?UTF-8?q?=E9=A1=B9=E7=9B=AE=E3=80=81=E7=BB=93=E6=9E=84=E4=BD=93=E7=BB=83?= =?UTF-8?q?=E4=B9=A0=E9=A2=98=E6=96=87=E6=A1=A3=EF=BC=8C=E4=BB=A5=E5=8F=8A?= =?UTF-8?q?tauri-app=E7=9A=84pnpm=E5=B7=A5=E4=BD=9C=E5=8C=BA=E9=85=8D?= =?UTF-8?q?=E7=BD=AE=E6=96=87=E4=BB=B6=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- part4/Cargo.toml | 6 + part4/src/main.rs | 3 + part4/struct练习题.md | 1129 +++++++++++++++++++++++++++++++++ tauri-app/pnpm-workspace.yaml | 3 + 4 files changed, 1141 insertions(+) create mode 100644 part4/Cargo.toml create mode 100644 part4/src/main.rs create mode 100644 part4/struct练习题.md create mode 100644 tauri-app/pnpm-workspace.yaml diff --git a/part4/Cargo.toml b/part4/Cargo.toml new file mode 100644 index 0000000..7ccbfcb --- /dev/null +++ b/part4/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "part4" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/part4/src/main.rs b/part4/src/main.rs new file mode 100644 index 0000000..e7a11a9 --- /dev/null +++ b/part4/src/main.rs @@ -0,0 +1,3 @@ +fn main() { + println!("Hello, world!"); +} diff --git a/part4/struct练习题.md b/part4/struct练习题.md new file mode 100644 index 0000000..04f581e --- /dev/null +++ b/part4/struct练习题.md @@ -0,0 +1,1129 @@ +# 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\),并实现以下方法: + +```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 +} +``` + +### 题目 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>) + +// 你的代码 + +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] +} +``` + +### 题目 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()); // 如果取消注释会怎样?为什么? +} +``` + +--- + +## 五、综合思考题 + +### 题目 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, // 缓存 +} +``` + +### 题目 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)` 有什么区别?在什么场景下应该使用元组结构体而不是普通元组? + +--- + +## 参考答案 + +> 请独立完成再查看答案。 + +
+点击展开答案 + +### 一、基础题 + +**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::() 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)— 已被移动给 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 + +
diff --git a/tauri-app/pnpm-workspace.yaml b/tauri-app/pnpm-workspace.yaml new file mode 100644 index 0000000..b1ecb22 --- /dev/null +++ b/tauri-app/pnpm-workspace.yaml @@ -0,0 +1,3 @@ +allowBuilds: + core-js: false + esbuild: false