实战总结
> 综合运用结构体知识,学习派生 Trait、常见错误诊断与调试技巧。
派生 Trait
常用派生宏
rust
▶ Run// Debug - 调试输出
#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
// Clone - 克隆
#[derive(Clone)]
struct Data {
value: i32,
}
// Copy - 复制(只适用于栈上类型)
#[derive(Copy, Clone)]
struct Point {
x: i32,
y: i32,
}
// PartialEq - 相等比较
#[derive(PartialEq)]
struct Color(u8, u8, u8);
// Eq - 完全相等(需要 PartialEq)
#[derive(PartialEq, Eq)]
struct Id(u32);
// PartialOrd - 部分排序
#[derive(PartialEq, PartialOrd)]
struct Score(f64);
// Ord - 完全排序(需要 PartialEq, Eq, PartialOrd)
#[derive(PartialEq, Eq, PartialOrd, Ord)]
struct Rank(u32);
// Hash - 哈希
#[derive(PartialEq, Eq, Hash)]
struct Key(String);
// 组合派生
#[derive(Debug, Clone, PartialEq, Eq)]
struct User {
id: u32,
name: String,
}使用派生 Trait
rust
▶ Run#[derive(Debug, Clone, PartialEq)]
struct Person {
name: String,
age: u32,
}
fn main() {
let p1 = Person {
name: String::from("Alice"),
age: 25,
};
// Debug
println!("p1: {:?}", p1);
println!("p1: {:#?}", p1); // 美化格式
// Clone
let p2 = p1.clone();
// PartialEq
let p3 = Person {
name: String::from("Alice"),
age: 25,
};
println!("p1 == p2: {}", p1 == p2);
println!("p1 == p3: {}", p1 == p3); // false(String 内容相等但不是同一实例)
}手动实现 Debug
rust
▶ Runstruct Point {
x: f64,
y: f64,
}
// 手动实现 Debug
impl std::fmt::Debug for Point {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Point({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 1.0, y: 2.0 };
println!("{:?}", p); // Point(1.0, 2.0)
}完整示例
示例 1:学生管理系统
rust
▶ Run#[derive(Debug)]
struct Student {
id: u32,
name: String,
grades: Vec<f64>,
}
impl Student {
fn new(id: u32, name: String) -> Self {
Self {
id,
name,
grades: Vec::new(),
}
}
fn add_grade(&mut self, grade: f64) {
self.grades.push(grade);
}
fn average(&self) -> f64 {
if self.grades.is_empty() {
return 0.0;
}
let sum: f64 = self.grades.iter().sum();
sum / self.grades.len() as f64
}
fn print_report(&self) {
println!("╔════════════════════════════════════╗");
println!("║ 学生成绩单 ║");
println!("╠════════════════════════════════════╣");
println!("║ ID: {:<4} │ 姓名:{:<10} ║", self.id, self.name);
println!("╠════════════════════════════════════╣");
print!("║ 成绩:");
for grade in &self.grades {
print!("{:.0} ", grade);
}
println!("║");
println!("╠════════════════════════════════════╣");
println!("║ 平均分:{:<22.2} ║", self.average());
println!("╚════════════════════════════════════╝");
}
}
fn main() {
let mut student = Student::new(1, String::from("张三"));
student.add_grade(90.0);
student.add_grade(85.0);
student.add_grade(95.0);
student.add_grade(88.0);
student.print_report();
}示例 2:几何图形系统
rust
▶ Run#[derive(Debug, Clone, Copy)]
struct Point {
x: f64,
y: f64,
}
#[derive(Debug, Clone)]
struct Rectangle {
top_left: Point,
bottom_right: Point,
}
impl Point {
fn new(x: f64, y: f64) -> Self {
Self { x, y }
}
fn distance_from_origin(&self) -> f64 {
(self.x.powi(2) + self.y.powi(2)).sqrt()
}
}
impl Rectangle {
fn new(x1: f64, y1: f64, x2: f64, y2: f64) -> Self {
Self {
top_left: Point::new(x1, y1.max(y2)),
bottom_right: Point::new(x2, y1.min(y2)),
}
}
fn width(&self) -> f64 {
(self.bottom_right.x - self.top_left.x).abs()
}
fn height(&self) -> f64 {
(self.top_left.y - self.bottom_right.y).abs()
}
fn area(&self) -> f64 {
self.width() * self.height()
}
fn contains(&self, point: &Point) -> bool {
point.x >= self.top_left.x
&& point.x <= self.bottom_right.x
&& point.y <= self.top_left.y
&& point.y >= self.bottom_right.y
}
fn translate(&mut self, dx: f64, dy: f64) {
self.top_left.x += dx;
self.top_left.y += dy;
self.bottom_right.x += dx;
self.bottom_right.y += dy;
}
}
fn main() {
let mut rect = Rectangle::new(0.0, 10.0, 20.0, 0.0);
println!("矩形:{:?}", rect);
println!("宽度:{}", rect.width());
println!("高度:{}", rect.height());
println!("面积:{}", rect.area());
let p1 = Point::new(5.0, 5.0);
let p2 = Point::new(25.0, 5.0);
println!("p1 在矩形内:{}", rect.contains(&p1));
println!("p2 在矩形内:{}", rect.contains(&p2));
rect.translate(10.0, 10.0);
println!("平移后:{:?}", rect);
}常见错误
错误 1:忘记 mut
rust
▶ Runstruct Counter {
count: u32,
}
fn main() {
let counter = Counter { count: 0 };
// counter.count += 1; // ❌ 错误:需要 mut
let mut counter = Counter { count: 0 };
counter.count += 1; // ✅ 正确
}错误信息:
error[E0594]: cannot assign to `counter.count`, which is not mutable
--> src/main.rs:7:5
|
7 | counter.count += 1;
| ^^^^^^^^^^^^^ cannot assign错误 2:字段类型不匹配
rust
▶ Runstruct Person {
name: String,
age: u32,
}
fn main() {
// let p = Person {
// name: "Alice", // ❌ 错误:需要 String,不是&str
// age: 25,
// };
let p = Person {
name: String::from("Alice"),
age: 25,
};
}错误 3:字段不完整
rust
▶ Runstruct Point {
x: i32,
y: i32,
}
fn main() {
// let p = Point { x: 1 }; // ❌ 错误:缺少 y 字段
let p = Point { x: 1, y: 2 };
}错误信息:
error[E0063]: missing field `y` in initializer of `Point`
--> src/main.rs:7:13
|
7 | let p = Point { x: 1 };
| ^^^^^ missing `y`错误 4:所有权问题
rust
▶ Runstruct User {
name: String,
}
fn main() {
let user1 = User {
name: String::from("alice"),
};
let user2 = User {
..user1 // user1.name 被移动
};
// println!("{}", user1.name); // ❌ 错误:已被移动
}调试技巧
打印结构体
rust
▶ Run#[derive(Debug)]
struct Person {
name: String,
age: u32,
}
fn main() {
let p = Person {
name: String::from("Alice"),
age: 25,
};
// 基本调试输出
println!("{:?}", p);
// 美化格式
println!("{:#?}", p);
// 使用 dbg!
dbg!(&p);
}自定义 Debug 输出
rust
▶ Runstruct Password(String);
impl std::fmt::Debug for Password {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
// 不显示实际密码
write!(f, "Password(****)")
}
}
fn main() {
let pwd = Password(String::from("secret123"));
println!("{:?}", pwd); // Password(****)
}练习
练习 1:图书管理
创建 Book 结构体,包含:
- 书名(title)
- 作者(author)
- 出版年份(year)
实现方法:
is_new(&self) -> bool- 判断是否是近 5 年出版
练习 2:圆形计算
创建 Circle 结构体,包含:
- 圆心(center: Point)
- 半径(radius: f64)
实现方法:
area(&self) -> f64- 计算面积circumference(&self) -> f64- 计算周长contains(&self, point: &Point) -> bool- 判断点是否在圆内
练习 3:购物车
创建 CartItem 和 ShoppingCart 结构体,实现:
- 添加商品
- 移除商品
- 计算总价
小结
结构体要点
| 概念 | 说明 |
|---|---|
| 命名字段 | struct Person { name: String } |
| 元组字段 | struct Color(i32, i32, i32) |
| 单元结构体 | struct Marker; |
| 方法 | fn method(&self) |
| 关联函数 | fn new() -> Self |
| 派生 Trait | #[derive(Debug, Clone)] |
self 的三种形式
| 形式 | 用途 | 示例 |
|---|---|---|
&self | 只读访问 | fn area(&self) |
&mut self | 修改数据 | fn push(&mut self) |
self | 消耗实例 | fn into_inner(self) |
最佳实践
- 使用有意义的字段名,提高代码可读性
- 实现
new构造函数,方便创建实例 - 派生常用 Trait(Debug, Clone, PartialEq)
- 区分&self 和&mut self,遵循借用规则
- 使用单元结构体作为标记类型
练习题
详见:练习题