Day 7: 结构体
今天是《30天精通Rust语言》系列的第七天。我们将学习结构体(Struct),这是Rust中定义自定义数据类型的主要方式。结构体允许你将相关数据组织在一起,形成有意义的数据单元。
2. 结构体定义与实例化
Section titled “2. 结构体定义与实例化”2.1 基本结构体定义
Section titled “2.1 基本结构体定义”struct User { username: String, email: String, sign_in_count: u64, active: bool,}2.2 创建结构体实例
Section titled “2.2 创建结构体实例”fn main() { let user1 = User { email: String::from("user@example.com"), username: String::from("rustacean"), active: true, sign_in_count: 1, };
println!("User: {}, {}", user1.username, user1.email);}2.3 可变结构体实例
Section titled “2.3 可变结构体实例”结构体实例需要整体标记为 mut 才能修改其字段:
fn main() { let mut user1 = User { email: String::from("user@example.com"), username: String::from("rustacean"), active: true, sign_in_count: 1, };
user1.email = String::from("new@example.com"); println!("Updated email: {}", user1.email);}注意:不能部分可变,字段级别的可变暂不支持。
3. 元组结构体
Section titled “3. 元组结构体”元组结构体(Tuple Struct)没有字段名,只有类型:
struct Color(i32, i32, i32);struct Point(i32, i32, i32);
fn main() { let black = Color(0, 0, 0); let origin = Point(0, 0, 0);
println!("Black: ({}, {}, {})", black.0, black.1, black.2); println!("Origin: ({}, {}, {})", origin.0, origin.1, origin.2);}元组结构体适合当你需要给元组起一个名字,但又不需要命名字段的场景。
4. 类单元结构体
Section titled “4. 类单元结构体”没有任何字段的结构体称为类单元结构体(Unit-like Struct),适合需要实现trait但不需要存储数据的场景:
struct AlwaysEqual;
fn main() { let subject = AlwaysEqual;}5. 结构体方法
Section titled “5. 结构体方法”方法(Method)是关联到结构体上的函数,第一个参数是 self,表示调用方法的结构体实例。
5.1 定义方法
Section titled “5.1 定义方法”struct Rectangle { width: u32, height: u32,}
impl Rectangle { // &self 表示不可变借用 fn area(&self) -> u32 { self.width * self.height }
// &mut self 表示可变借用 fn scale(&mut self, factor: u32) { self.width *= factor; self.height *= factor; }
// 静态方法(关联函数),不借用了self fn new(width: u32, height: u32) -> Rectangle { Rectangle { width, height } }}
fn main() { let mut rect = Rectangle::new(30, 50); println!("Area: {}", rect.area());
rect.scale(2); println!("Scaled: {}x{}", rect.width, rect.height);}5.2 方法与所有权
Section titled “5.2 方法与所有权”方法可以获取 self 的所有权,或借用 self:
impl Rectangle { // 消耗self,调用后rect不再可用 fn destroy(self) { println!("Destroying Rectangle {}x{}", self.width, self.height); }
// 借用self(不可变) fn describe(&self) { println!("Rectangle: {}x{}", self.width, self.height); }}6. 关联函数
Section titled “6. 关联函数”关联函数是不以 self 为参数的函数,常用于构造器模式:
impl Rectangle { fn square(size: u32) -> Rectangle { Rectangle { width: size, height: size } }}
fn main() { let sq = Rectangle::square(10); println!("Square: {}x{}", sq.width, sq.height);}7. 结构体与所有权
Section titled “7. 结构体与所有权”结构体字段的所有权遵循所有权规则:
struct User { username: String, email: String,}
fn main() { let user = User { email: String::from("user@example.com"), username: String::from("rustacean"), }; // user离开作用域时,username和email都会被drop}如果字段需要引用而非拥有,需要使用生命周期注解(下一章内容)。
8. 实用示例
Section titled “8. 实用示例”让我们创建一个完整的示例,综合运用结构体的各种特性:
struct BankAccount { account_id: u32, holder_name: String, balance: f64,}
impl BankAccount { fn new(id: u32, name: &str) -> BankAccount { BankAccount { account_id: id, holder_name: String::from(name), balance: 0.0, } }
fn deposit(&mut self, amount: f64) { if amount > 0.0 { self.balance += amount; println!("Deposited {}. New balance: {}", amount, self.balance); } else { println!("Invalid deposit amount"); } }
fn withdraw(&mut self, amount: f64) { if amount > self.balance { println!("Insufficient balance!"); } else if amount <= 0.0 { println!("Invalid withdrawal amount"); } else { self.balance -= amount; println!("Withdrew {}. New balance: {}", amount, self.balance); } }
fn balance(&self) -> f64 { self.balance }}
fn main() { let mut account = BankAccount::new(1001, "Alice"); println!("Account created for {}", account.holder_name);
account.deposit(1000.0); account.withdraw(300.0); println!("Final balance: {}", account.balance());}9. 实践练习
Section titled “9. 实践练习”今天的实践任务:
- 定义一个
Book结构体,包含title(String)、author(String)、year(u32)字段。 - 为
Book实现一个new关联函数和info方法。 - 创建两个
Book实例并调用它们的方法。 - 实现一个
Stack<T>结构体(泛型),包含push、pop、is_empty方法。 - 修改
BankAccount示例,添加利息计算功能。
10. 常见问题
Section titled “10. 常见问题”问:结构体和方法为什么不放在同一个文件中?
答:Rust推荐将 impl 块与结构体定义放在同一个文件,这是标准做法。在大型项目中,你可能会用多个 impl 块实现不同的 trait。
问:什么时候用元组结构体而不是普通结构体?
答:当字段有独立含义但不需要命名时,或作为类型别名时使用元组结构体。例如 struct Point(f32, f32) 比 struct Point { x: f32, y: f32 } 更简洁。
11. 总结
Section titled “11. 总结”今天我们学习了Rust的结构体:
- 结构体是自定义数据类型,将相关字段组合在一起。
- 元组结构体和类单元结构体提供了不同的组织方式。
- 方法通过
impl块定义,第一个参数是self。 - 关联函数(静态方法)常用于构造器模式。
- 结构体字段遵循所有权规则。
明天我们将学习枚举与模式匹配,这是Rust中另一个强大的特性。加油!