【Rust编程:从小白入坑】Rust结构体(Struct)详解
引言
在前面的文章中,我们学习了基本数据类型和切片。今天我们将学习如何使用结构体(Struct)来创建自定义的数据类型,这是Rust中组织和封装数据的重要方式。
一、结构体基础
1.1 定义结构体
结构体类似于元组,但更灵活——每个数据都有自己的名字。
struct User {username: String,email: String,age: u32,active: bool,
}fn main() {let user1 = User {username: String::from("张三"),email: String::from("zhangsan@example.com"),age: 25,active: true,};println!("用户名: {}", user1.username);println!("邮箱: {}", user1.email);println!("年龄: {}", user1.age);println!("是否激活: {}", user1.active);
}
语法解析:
- struct:定义结构体的关键字
- User:结构体名称(使用大驼峰命名)
- username: String:字段名和类型
- 实例化时需要为所有字段赋值
1.2 访问结构体字段
fn main() {let user = User {username: String::from("李四"),email: String::from("lisi@example.com"),age: 30,active: true,};// 使用点号访问字段println!("用户名: {}", user.username);println!("年龄: {}", user.age);
}struct User {username: String,email: String,age: u32,active: bool,
}
1.3 可变结构体实例
fn main() {let mut user = User {username: String::from("王五"),email: String::from("wangwu@example.com"),age: 28,active: true,};println!("原邮箱: {}", user.email);// 修改字段user.email = String::from("newemail@example.com");user.age = 29;println!("新邮箱: {}", user.email);println!("新年龄: {}", user.age);
}struct User {username: String,email: String,age: u32,active: bool,
}
注意:整个实例必须是可变的,Rust不允许只标记某些字段为可变。
二、结构体的创建
2.1 字段初始化简写
当变量名与字段名相同时,可以使用简写:
fn main() {let username = String::from("赵六");let email = String::from("zhaoliu@example.com");// ❌ 繁琐的方式let user1 = User {username: username.clone(),email: email.clone(),age: 35,active: true,};// ✅ 简写方式let user2 = User {username,email,age: 35,active: true,};println!("{}", user2.username);
}struct User {username: String,email: String,age: u32,active: bool,
}
2.2 使用构造函数
struct User {username: String,email: String,age: u32,active: bool,
}fn build_user(username: String, email: String) -> User {User {username,email,age: 0,active: true,}
}fn main() {let user = build_user(String::from("孙七"),String::from("sunqi@example.com"),);println!("用户: {} - {}", user.username, user.email);
}
2.3 结构体更新语法
从其他实例创建新实例:
fn main() {let user1 = User {username: String::from("user1"),email: String::from("user1@example.com"),age: 25,active: true,};// ❌ 繁琐的方式let user2 = User {username: String::from("user2"),email: user1.email.clone(),age: user1.age,active: user1.active,};// ✅ 使用结构体更新语法let user3 = User {username: String::from("user3"),email: String::from("user3@example.com"),..user1  // 其余字段从user1获取};println!("user3年龄: {}", user3.age);println!("user3激活状态: {}", user3.active);
}struct User {username: String,email: String,age: u32,active: bool,
}
注意:..user1必须放在最后,并且会移动String类型的字段。
三、元组结构体
3.1 基本元组结构体
没有字段名的结构体:
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.0, black.1, black.2);println!("原点: ({}, {}, {})", origin.0, origin.1, origin.2);// black和origin是不同的类型,即使结构相同// let color: Color = origin;  // ❌ 类型不匹配
}
3.2 元组结构体的应用
struct Meters(f64);
struct Kilometers(f64);fn main() {let distance_m = Meters(1000.0);let distance_km = Kilometers(1.0);println!("距离(米): {}", distance_m.0);println!("距离(千米): {}", distance_km.0);// 类型安全:不能混用// let total = distance_m.0 + distance_km.0;  // 需要转换
}
四、单元结构体
4.1 单元结构体定义
没有任何字段的结构体:
struct AlwaysEqual;fn main() {let subject = AlwaysEqual;// 单元结构体常用于实现trait,但不需要存储数据
}五、打印结构体
5.1 使用Debug trait
#[derive(Debug)]
struct Rectangle {width: u32,height: u32,
}fn main() {let rect = Rectangle {width: 30,height: 50,};// {:?} 格式化println!("矩形: {:?}", rect);// {:#?} 美化打印println!("矩形(美化):\n{:#?}", rect);
}5.2 使用dbg!宏
#[derive(Debug)]
struct Rectangle {width: u32,height: u32,
}fn main() {let scale = 2;let rect = Rectangle {width: dbg!(30 * scale),  // 打印表达式和结果height: 50,};dbg!(&rect);  // 打印rect(借用,不获取所有权)
}六、方法
6.1 定义方法
方法在impl块中定义:
#[derive(Debug)]
struct Rectangle {width: u32,height: u32,
}impl Rectangle {// 方法:第一个参数是selffn area(&self) -> u32 {self.width * self.height}fn perimeter(&self) -> u32 {2 * (self.width + self.height)}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,};println!("rect1的面积: {}", rect1.area());println!("rect1的周长: {}", rect1.perimeter());println!("rect1能容纳rect2吗? {}", rect1.can_hold(&rect2));
}6.2 self的不同形式
impl Rectangle {// &self: 不可变借用fn area(&self) -> u32 {self.width * self.height}// &mut self: 可变借用fn expand(&mut self, amount: u32) {self.width += amount;self.height += amount;}// self: 获取所有权(少用)fn consume(self) -> u32 {println!("消费Rectangle");self.width * self.height}
}fn main() {let mut rect = Rectangle {width: 30,height: 50,};println!("面积: {}", rect.area());rect.expand(10);println!("扩展后: {}x{}", rect.width, rect.height);// let area = rect.consume();// println!("{}", rect.width);  // ❌ rect已被移动
}#[derive(Debug)]
struct Rectangle {width: u32,height: u32,
}6.3 多个参数的方法
#[derive(Debug)]
struct Rectangle {width: u32,height: u32,
}impl Rectangle {fn area(&self) -> u32 {self.width * self.height}fn can_hold(&self, other: &Rectangle) -> bool {self.width > other.width && self.height > other.height}fn set_dimensions(&mut self, width: u32, height: u32) {self.width = width;self.height = 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能容纳rect2吗? {}", rect1.can_hold(&rect2));println!("rect1能容纳rect3吗? {}", rect1.can_hold(&rect3));
}七、关联函数
7.1 定义关联函数
不以self作为参数的函数,常用作构造器:
struct Rectangle {width: u32,height: u32,
}impl Rectangle {// 关联函数fn new(width: u32, height: u32) -> Rectangle {Rectangle { width, height }}fn square(size: u32) -> Rectangle {Rectangle {width: size,height: size,}}fn default() -> Rectangle {Rectangle {width: 0,height: 0,}}
}fn main() {// 使用::调用关联函数let rect1 = Rectangle::new(30, 50);let rect2 = Rectangle::square(20);let rect3 = Rectangle::default();println!("rect1: {}x{}", rect1.width, rect1.height);println!("rect2: {}x{}", rect2.width, rect2.height);println!("rect3: {}x{}", rect3.width, rect3.height);
}7.2 多个impl块
同一个结构体可以有多个impl块:
struct Rectangle {width: u32,height: u32,
}// 第一个impl块:构造函数
impl Rectangle {fn new(width: u32, height: u32) -> Rectangle {Rectangle { width, height }}fn square(size: u32) -> Rectangle {Rectangle {width: size,height: size,}}
}// 第二个impl块:计算方法
impl Rectangle {fn area(&self) -> u32 {self.width * self.height}fn perimeter(&self) -> u32 {2 * (self.width + self.height)}
}// 第三个impl块:比较方法
impl Rectangle {fn can_hold(&self, other: &Rectangle) -> bool {self.width > other.width && self.height > other.height}fn is_larger_than(&self, other: &Rectangle) -> bool {self.area() > other.area()}
}fn main() {let rect = Rectangle::new(30, 50);println!("面积: {}", rect.area());
}八、实战示例
示例1:学生管理系统
#[derive(Debug)]
struct Student {name: String,age: u8,grade: u8,scores: Vec<f32>,
}impl Student {fn new(name: String, age: u8, grade: u8) -> Student {Student {name,age,grade,scores: Vec::new(),}}fn add_score(&mut self, score: f32) {self.scores.push(score);}fn average_score(&self) -> f32 {if self.scores.is_empty() {return 0.0;}let sum: f32 = self.scores.iter().sum();sum / self.scores.len() as f32}fn is_passing(&self) -> bool {self.average_score() >= 60.0}fn summary(&self) -> String {format!("学生: {}, 年龄: {}, 年级: {}, 平均分: {:.2}, 状态: {}",self.name,self.age,self.grade,self.average_score(),if self.is_passing() { "及格" } else { "不及格" })}
}fn main() {let mut student = Student::new(String::from("张三"),16,10,);student.add_score(85.0);student.add_score(92.0);student.add_score(78.0);student.add_score(88.0);println!("{}", student.summary());println!("详细信息: {:#?}", student);
}示例2:银行账户
#[derive(Debug)]
struct BankAccount {account_number: String,holder_name: String,balance: f64,
}impl BankAccount {fn new(account_number: String, holder_name: String) -> BankAccount {BankAccount {account_number,holder_name,balance: 0.0,}}fn deposit(&mut self, amount: f64) -> Result<(), String> {if amount <= 0.0 {return Err(String::from("存款金额必须大于0"));}self.balance += amount;println!("存款成功!当前余额: {:.2}", self.balance);Ok(())}fn withdraw(&mut self, amount: f64) -> Result<(), String> {if amount <= 0.0 {return Err(String::from("取款金额必须大于0"));}if amount > self.balance {return Err(String::from("余额不足"));}self.balance -= amount;println!("取款成功!当前余额: {:.2}", self.balance);Ok(())}fn check_balance(&self) -> f64 {self.balance}fn transfer(&mut self, to: &mut BankAccount, amount: f64) -> Result<(), String> {self.withdraw(amount)?;to.deposit(amount)?;println!("转账成功!");Ok(())}
}fn main() {let mut account1 = BankAccount::new(String::from("001"),String::from("张三"),);let mut account2 = BankAccount::new(String::from("002"),String::from("李四"),);account1.deposit(1000.0).unwrap();account1.withdraw(200.0).unwrap();println!("账户1余额: {:.2}", account1.check_balance());account1.transfer(&mut account2, 300.0).unwrap();println!("账户1余额: {:.2}", account1.check_balance());println!("账户2余额: {:.2}", account2.check_balance());
}示例3:图书管理
#[derive(Debug, Clone)]
struct Book {title: String,author: String,isbn: String,available: bool,
}impl Book {fn new(title: String, author: String, isbn: String) -> Book {Book {title,author,isbn,available: true,}}fn borrow(&mut self) -> Result<(), String> {if !self.available {return Err(format!("《{}》已被借出", self.title));}self.available = false;println!("《{}》借阅成功", self.title);Ok(())}fn return_book(&mut self) {self.available = true;println!("《{}》归还成功", self.title);}fn info(&self) -> String {format!("书名: {}, 作者: {}, ISBN: {}, 状态: {}",self.title,self.author,self.isbn,if self.available { "可借" } else { "已借出" })}
}#[derive(Debug)]
struct Library {name: String,books: Vec<Book>,
}impl Library {fn new(name: String) -> Library {Library {name,books: Vec::new(),}}fn add_book(&mut self, book: Book) {println!("添加图书: {}", book.title);self.books.push(book);}fn list_available_books(&self) {println!("\n{} - 可借图书:", self.name);for book in &self.books {if book.available {println!("  - {}", book.info());}}}fn find_book(&mut self, title: &str) -> Option<&mut Book> {self.books.iter_mut().find(|b| b.title == title)}
}fn main() {let mut library = Library::new(String::from("市图书馆"));library.add_book(Book::new(String::from("Rust编程"),String::from("作者A"),String::from("ISBN-001"),));library.add_book(Book::new(String::from("深入理解Rust"),String::from("作者B"),String::from("ISBN-002"),));library.list_available_books();if let Some(book) = library.find_book("Rust编程") {book.borrow().unwrap();}library.list_available_books();
}九、结构体的所有权
9.1 结构体拥有数据
struct User {username: String,  // 拥有Stringemail: String,     // 拥有Stringage: u32,          // 拥有u32
}fn main() {let user = User {username: String::from("user1"),email: String::from("user1@example.com"),age: 25,};// user拥有所有数据// 当user离开作用域时,所有数据都会被清理
}9.2 结构体中的引用
// 需要生命周期注解(后续章节详细讲解)
struct User<'a> {username: &'a str,email: &'a str,
}fn main() {let name = String::from("user1");let email = String::from("user1@example.com");let user = User {username: &name,email: &email,};println!("{}: {}", user.username, user.email);
}十、练习题
练习1:点和距离
#[derive(Debug)]
struct Point {x: f64,y: f64,
}impl Point {fn new(x: f64, y: f64) -> Point {Point { x, y }}fn distance_from_origin(&self) -> f64 {(self.x * self.x + self.y * self.y).sqrt()}fn distance_to(&self, other: &Point) -> f64 {let dx = self.x - other.x;let dy = self.y - other.y;(dx * dx + dy * dy).sqrt()}
}fn main() {let p1 = Point::new(3.0, 4.0);let p2 = Point::new(0.0, 0.0);println!("点1到原点距离: {:.2}", p1.distance_from_origin());println!("点1到点2距离: {:.2}", p1.distance_to(&p2));
}练习2:温度转换
struct Temperature {celsius: f64,
}impl Temperature {fn from_celsius(celsius: f64) -> Temperature {Temperature { celsius }}fn from_fahrenheit(fahrenheit: f64) -> Temperature {let celsius = (fahrenheit - 32.0) / 1.8;Temperature { celsius }}fn to_fahrenheit(&self) -> f64 {self.celsius * 1.8 + 32.0}fn to_kelvin(&self) -> f64 {self.celsius + 273.15}
}fn main() {let temp1 = Temperature::from_celsius(25.0);println!("25°C = {}°F", temp1.to_fahrenheit());println!("25°C = {}K", temp1.to_kelvin());let temp2 = Temperature::from_fahrenheit(77.0);println!("77°F = {}°C", temp2.celsius);
}练习3:待办事项
#[derive(Debug)]
struct Todo {title: String,completed: bool,
}impl Todo {fn new(title: String) -> Todo {Todo {title,completed: false,}}fn complete(&mut self) {self.completed = true;}fn status(&self) -> &str {if self.completed {"完成"} else {"未完成"}}
}fn main() {let mut todo = Todo::new(String::from("学习Rust结构体"));println!("任务: {} - {}", todo.title, todo.status());todo.complete();println!("任务: {} - {}", todo.title, todo.status());
}十一、常见错误
错误1:忘记mut
struct Counter {count: u32,
}impl Counter {fn increment(&mut self) {self.count += 1;}
}fn main() {// ❌ 错误:需要mut// let counter = Counter { count: 0 };// counter.increment();// ✅ 正确let mut counter = Counter { count: 0 };counter.increment();
}错误2:方法调用语法
impl Rectangle {fn area(&self) -> u32 {self.width * self.height}
}struct Rectangle {width: u32,height: u32,
}fn main() {let rect = Rectangle { width: 30, height: 50 };// ❌ 错误// let area = Rectangle::area(rect);// ✅ 正确let area = rect.area();println!("{}", area);
}总结
本文学习了:
✅ 结构体的定义和使用
✅ 字段访问和修改
✅ 元组结构体和单元结构体
✅ 方法和关联函数
✅ impl块的使用
✅ 实战示例
核心要点:
- struct定义自定义数据类型
- 使用点号访问字段
- impl块定义方法和关联函数
- self表示实例本身
- 关联函数使用::调用
