当前位置: 首页 > news >正文

【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块的使用
✅ 实战示例

核心要点

  1. struct定义自定义数据类型
  2. 使用点号访问字段
  3. impl块定义方法和关联函数
  4. self表示实例本身
  5. 关联函数使用::调用
http://www.dtcms.com/a/549440.html

相关文章:

  • 【开题答辩实录分享】以《足球社区微信小程序》为例进行答辩实录分享
  • 哪个网站能帮助做试卷个人免费注册公司
  • 万国手表网站亚马逊企业网站建设
  • java对图片进行表单,生成本地图片或者流式输出
  • Python 虚拟环境:告别依赖冲突的实用指南
  • LP8773S第二代准谐振PWM控制器12V芯片内部框架图及应用信息介绍
  • Ruby Mysql:深度解析Ruby与MySQL的交互
  • 清空回收站后的文件还能恢复吗?分析原理,讲清方法
  • 怎样做外部网站推广郑州网站建设知识分享
  • 赋能采购革新:星合智联如何为企业开启TI芯片直采数字化新体验?
  • 构建可持续私域运营体系:从黑五营销到长期用户沉淀指南
  • 科技部网站php做网站不兼容ie8
  • 玉田网站制作wordpress百度mlp
  • Vite 大型项目优化方案
  • git处理分支
  • ELK日志系统部署与使用(Elasticsearch、Logstash、Kibana)
  • Gitee:代码管理
  • 购物网站建设论文织梦cms网站迁移
  • CP网站建设搭建需要多少钱大冶市城乡建设局网站
  • FramelessBaseWindow - 通用Qt无边框窗口基类
  • seo查询 站长工具利用织梦搭网站
  • 第238题 除自身以外数组的乘积
  • Vue 状态管理库相关收录
  • CG-5重力仪外壳漏电怎么办?
  • 商务网站规划与建设课设的项目需求seo网站有优化培训吗
  • 从 VLDB‘25 看向量数据库发展方向:行业观察与技术前瞻
  • 生鲜电商企业微信私域代运营:从去中心化运营看微盛AI·企微管家SCRM适配案例
  • 企业微信如何正确营销获客?精准定位与场景触达的实践框架
  • 企业微信私有化服务商怎么选?从数据安全与定制化需求看适配方向
  • 【百度AI】Postman调用OCR服务-解决官方教程请求失败问题