Rust进阶:结构体与枚举实战
·

在掌握了Rust的基础知识后,让我们继续深入探索这门语言更强大的特性。本文将带你了解结构体、枚举、模式匹配等核心概念,并展示如何在实际项目中运用它们。
第五章:使用结构体组织相关联的数据
5.1 定义和实例化结构体
结构体是Rust中创建自定义数据类型的主要方式,让我们能够将相关的数据组合在一起。
基本结构体定义:
// 定义结构体
struct User {
username: String,
email: String,
sign_in_count: u64,
active: bool,
}
// 实例化结构体
fn main() {
let user1 = User {
email: String::from("someone@example.com"),
username: String::from("someusername123"),
active: true,
sign_in_count: 1,
};
println!("用户邮箱:{}", user1.email);
// 创建可变的结构体实例
let mut user2 = User {
email: String::from("another@example.com"),
username: String::from("anotheruser"),
active: true,
sign_in_count: 1,
};
user2.email = String::from("newemail@example.com");
}
结构体更新语法:
let user3 = User {
email: String::from("user3@example.com"),
username: String::from("userthree"),
..user1 // 使用user1的其余字段值
};
5.2 方法语法
方法是在结构体上下文中定义的函数,第一个参数总是self。
#[derive(Debug)]
struct Rectangle {
width: u32,
height: u32,
}
// 在impl块中定义方法
impl Rectangle {
// 关联函数(静态方法)
fn square(size: u32) -> Rectangle {
Rectangle {
width: size,
height: size,
}
}
// 方法 - 借用self
fn area(&self) -> u32 {
self.width * self.height
}
// 方法 - 可变借用self
fn resize(&mut self, width: u32, height: u32) {
self.width = width;
self.height = height;
}
// 方法 - 获取self
fn consume(self) -> String {
format!("消耗了{}x{}的矩形", self.width, self.height)
}
fn can_hold(&self, other: &Rectangle) -> bool {
self.width > other.width && self.height > other.height
}
}
fn main() {
let mut rect = Rectangle {
width: 30,
height: 50,
};
println!("矩形面积:{}", rect.area());
rect.resize(40, 60);
println!("调整后面积:{}", rect.area());
let rect2 = Rectangle {
width: 20,
height: 40,
};
println!("rect能否容纳rect2:{}", rect.can_hold(&rect2));
let square = Rectangle::square(25);
println!("正方形:{:?}", square);
}
第六章:枚举和模式匹配
6.1 定义枚举
枚举允许我们定义一个类型,其值可以是几个不同的变体之一。
// 基本枚举
enum IpAddrKind {
V4,
V6,
}
// 带数据的枚举
enum IpAddr {
V4(u8, u8, u8, u8),
V6(String),
}
// 复杂枚举示例
enum Message {
Quit, // 无关联数据
Move { x: i32, y: i32 }, // 匿名结构体
Write(String), // 单个String
ChangeColor(i32, i32, i32), // 三个i32值
}
// 为枚举定义方法
impl Message {
fn call(&self) {
// 方法实现
}
}
fn main() {
let four = IpAddrKind::V4;
let six = IpAddrKind::V6;
let home = IpAddr::V4(127, 0, 0, 1);
let loopback = IpAddr::V6(String::from("::1"));
let msg = Message::Write(String::from("hello"));
msg.call();
}
6.2 match控制流运算符
match是Rust中强大的控制流运算符,允许我们将值与一系列模式进行比较。
enum Coin {
Penny,
Nickel,
Dime,
Quarter(UsState),
}
#[derive(Debug)]
enum UsState {
Alabama,
Alaska,
// ... 其他州
}
fn value_in_cents(coin: Coin) -> u8 {
match coin {
Coin::Penny => {
println!("幸运的一分钱!");
1
}
Coin::Nickel => 5,
Coin::Dime => 10,
Coin::Quarter(state) => {
println!("来自{:?}州的25分硬币", state);
25
}
}
}
// 匹配Option<T>
fn plus_one(x: Option<i32>) -> Option<i32> {
match x {
None => None,
Some(i) => Some(i + 1),
}
}
fn main() {
let coin = Coin::Quarter(UsState::Alaska);
println!("硬币价值:{}分", value_in_cents(coin));
let five = Some(5);
let six = plus_one(five);
let none = plus_one(None);
println!("six: {:?}, none: {:?}", six, none);
}
6.3 if let简洁控制流
if let语法让我们可以处理只关心一种模式而忽略其他模式的情况。
fn main() {
let some_u8_value = Some(3u8);
// 使用match
match some_u8_value {
Some(3) => println!("三"),
_ => (),
}
// 使用if let - 更简洁
if let Some(3) = some_u8_value {
println!("三");
}
// if let else 示例
let mut count = 0;
let coin = Coin::Quarter(UsState::Alaska);
if let Coin::Quarter(state) = coin {
println!("来自{:?}州的25分硬币", state);
} else {
count += 1;
}
}
第七章:模块系统
7.1 包和Crate
项目组织:
my_project/
├── Cargo.toml
└── src/
├── main.rs
├── lib.rs
└── front_of_house/
├── hosting.rs
└── serving.rs
7.2 定义模块控制作用域和私有性
src/lib.rs:
// 模块声明
mod front_of_house;
// 使用pub关键字使模块公开
pub mod back_of_house {
// 公开枚举的所有变体自动成为公开的
pub enum Appetizer {
Soup,
Salad,
}
// 结构体字段默认是私有的
pub struct Breakfast {
pub toast: String, // 公开字段
seasonal_fruit: String, // 私有字段
}
impl Breakfast {
// 关联函数
pub fn summer(toast: &str) -> Breakfast {
Breakfast {
toast: String::from(toast),
seasonal_fruit: String::from("peaches"),
}
}
}
}
// 使用use关键字引入路径到作用域
use crate::front_of_house::hosting;
pub fn eat_at_restaurant() {
// 绝对路径
crate::front_of_house::hosting::add_to_waitlist();
// 相对路径
front_of_house::hosting::add_to_waitlist();
// 使用use引入的路径
hosting::add_to_waitlist();
// 点餐示例
let mut meal = back_of_house::Breakfast::summer("Rye");
meal.toast = String::from("Wheat");
println!("我要{}吐司", meal.toast);
let order1 = back_of_house::Appetizer::Soup;
let order2 = back_of_house::Appetizer::Salad;
}
src/front_of_house/hosting.rs:
pub fn add_to_waitlist() {}
fn seat_at_table() {}
src/front_of_house/serving.rs:
fn take_order() {}
fn serve_order() {}
fn take_payment() {}
第八章:常见集合
8.1 向量(Vector)
fn main() {
// 创建向量
let mut v: Vec<i32> = Vec::new();
let v2 = vec![1, 2, 3]; // 使用宏创建
// 添加元素
v.push(5);
v.push(6);
v.push(7);
// 访问元素
let third: &i32 = &v[2];
println!("第三个元素是:{}", third);
match v.get(2) {
Some(third) => println!("第三个元素是:{}", third),
None => println!("没有第三个元素"),
}
// 遍历向量
for i in &v {
println!("{}", i);
}
// 遍历并修改
for i in &mut v {
*i += 50;
}
// 使用枚举存储多种类型
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
}
8.2 字符串(String)
fn main() {
// 创建字符串
let mut s = String::new();
let s1 = "初始内容".to_string();
let s2 = String::from("另一个字符串");
// 更新字符串
s.push_str("foo");
s.push('b');
let s3 = s1 + &s2; // 注意:s1被移动了,不能再使用
// 使用format!宏
let s4 = format!("{}-{}-{}", "tic", "tac", "toe");
// 字符串切片
let hello = "Здравствуйте";
let s5 = &hello[0..4]; // 前4个字节
// 遍历字符串
for c in "नमस्ते".chars() {
println!("{}", c);
}
for b in "नमस्ते".bytes() {
println!("{}", b);
}
}
8.3 哈希映射(HashMap)
use std::collections::HashMap;
fn main() {
// 创建哈希映射
let mut scores = HashMap::new();
// 插入值
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// 访问值
let team_name = String::from("Blue");
let score = scores.get(&team_name);
// 遍历
for (key, value) in &scores {
println!("{}: {}", key, value);
}
// 更新值
scores.insert(String::from("Blue"), 25); // 覆盖
// 只在键不存在时插入
scores.entry(String::from("Yellow")).or_insert(50);
scores.entry(String::from("Red")).or_insert(0);
// 根据旧值更新
let text = "hello world wonderful world";
let mut map = HashMap::new();
for word in text.split_whitespace() {
let count = map.entry(word).or_insert(0);
*count += 1;
}
println!("{:?}", map);
}
第九章:错误处理
9.1 不可恢复的错误与panic!
fn main() {
// 恐慌宏 - 程序会终止执行
// panic!("崩溃了!");
// 在Cargo.toml中设置 panic = 'abort' 可以立即终止程序
}
9.2 可恢复的错误与Result
use std::fs::File;
use std::io::{self, Read};
// 传播错误的简单方式
fn read_username_from_file() -> Result<String, io::Error> {
let mut s = String::new();
File::open("hello.txt")?.read_to_string(&mut s)?;
Ok(s)
}
// 更复杂的错误处理
fn divide(x: f64, y: f64) -> Result<f64, String> {
if y == 0.0 {
Err(String::from("除数不能为零"))
} else {
Ok(x / y)
}
}
fn main() {
// 使用match处理Result
let result = divide(10.0, 2.0);
match result {
Ok(value) => println!("结果是:{}", value),
Err(e) => println!("错误:{}", e),
}
// 使用unwrap和expect
let f = File::open("hello.txt").unwrap(); // 错误时panic
let f = File::open("hello.txt").expect("无法打开文件"); // 带自定义消息的panic
// 使用?运算符传播错误
match read_username_from_file() {
Ok(username) => println!("用户名:{}", username),
Err(e) => println!("读取文件错误:{}", e),
}
}
实战项目:构建简单的待办事项应用
让我们用学到的知识构建一个简单的命令行待办事项应用:
Cargo.toml:
[package]
name = "todo_app"
version = "0.1.0"
edition = "2021"
[dependencies]
src/main.rs:
use std::collections::HashMap;
use std::io;
#[derive(Debug)]
enum TaskStatus {
Pending,
Completed,
}
struct TodoList {
tasks: HashMap<u32, (String, TaskStatus)>,
next_id: u32,
}
impl TodoList {
fn new() -> TodoList {
TodoList {
tasks: HashMap::new(),
next_id: 1,
}
}
fn add_task(&mut self, description: String) -> u32 {
let id = self.next_id;
self.tasks.insert(id, (description, TaskStatus::Pending));
self.next_id += 1;
id
}
fn complete_task(&mut self, id: u32) -> Result<(), String> {
match self.tasks.get_mut(&id) {
Some((_, status)) => {
*status = TaskStatus::Completed;
Ok(())
}
None => Err(format!("任务ID {} 不存在", id)),
}
}
fn list_tasks(&self) {
if self.tasks.is_empty() {
println!("没有待办事项");
return;
}
for (id, (description, status)) in &self.tasks {
let status_str = match status {
TaskStatus::Pending => "待完成",
TaskStatus::Completed => "已完成",
};
println!("{}: {} [{}]", id, description, status_str);
}
}
fn remove_task(&mut self, id: u32) -> Result<(), String> {
match self.tasks.remove(&id) {
Some(_) => Ok(()),
None => Err(format!("任务ID {} 不存在", id)),
}
}
}
fn main() {
let mut todo_list = TodoList::new();
loop {
println!("\n=== 待办事项应用 ===");
println!("1. 添加任务");
println!("2. 完成任务");
println!("3. 列出任务");
println!("4. 删除任务");
println!("5. 退出");
println!("请选择操作:");
let mut choice = String::new();
io::stdin()
.read_line(&mut choice)
.expect("读取输入失败");
match choice.trim() {
"1" => {
println!("请输入任务描述:");
let mut description = String::new();
io::stdin()
.read_line(&mut description)
.expect("读取输入失败");
let id = todo_list.add_task(description.trim().to_string());
println!("已添加任务,ID: {}", id);
}
"2" => {
println!("请输入要完成的任务ID:");
let mut id_input = String::new();
io::stdin()
.read_line(&mut id_input)
.expect("读取输入失败");
match id_input.trim().parse() {
Ok(id) => {
match todo_list.complete_task(id) {
Ok(()) => println!("任务已完成"),
Err(e) => println!("错误:{}", e),
}
}
Err(_) => println!("无效的ID"),
}
}
"3" => {
todo_list.list_tasks();
}
"4" => {
println!("请输入要删除的任务ID:");
let mut id_input = String::new();
io::stdin()
.read_line(&mut id_input)
.expect("读取输入失败");
match id_input.trim().parse() {
Ok(id) => {
match todo_list.remove_task(id) {
Ok(()) => println!("任务已删除"),
Err(e) => println!("错误:{}", e),
}
}
Err(_) => println!("无效的ID"),
}
}
"5" => {
println!("再见!");
break;
}
_ => println!("无效的选择,请重试"),
}
}
}
记住,Rust的学习是一个渐进的过程。每个新概念都可能需要时间来完全理解,但这种投资最终会得到丰厚的回报。继续编码,享受Rust带来的安全性和性能优势!
继续你的Rust之旅!🚀
版权声明:本教程仅供学习使用,转载请注明出处。
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)