Rust基础[part8]_模式匹配、常见集合

AI-摘要
sonia33 GPT
AI初始化中...
介绍自己 🙈
生成本文简介 👋
推荐相关文章 📖
前往主页 🏠
前往爱发电购买
Rust基础[part8]_模式匹配、常见集合
SoniaChenRust基础[part8]_模式匹配、常见集合
模式匹配
检查数据结构,提高代码的可读性和简洁性,减少错误,尤其在处理复杂数据结构的时候
基础模式匹配
fn match_example() {
let x = 5;
// 必须要有所有可能的case
match x {
1 => println!("one"),
2 => println!("two"),
3 => println!("three"),
_ => println!("something else"),
}
}
守卫
在模式匹配中,可以总使用守卫来添加额外的条件判断。
let x = 5;
match x {
n if n > 5 => println!("x is greater than 5"),
_ => println!("x is less than or equal to 5"),
}
绑定
在模式匹配中,可以使用绑定来将模式中的值绑定到变量上
@ 0..=5
这个是用来指定范围的
// 绑定:可以把匹配到的值绑定到一个变量上
let enum1 = MyEnum::Hello { a: 5 };
match enum1 {
MyEnum::Hello { a: val @ 0..=5 } => println!("x is {}", val),
MyEnum::Hello { a: val @ 5.. } => println!("x is {}", val),
MyEnum::B(s) => println!("s is {}", s),
MyEnum::C => println!("C"),
_ => println!("something else"),
}
应用场景
-
处理错误
/** * 匹配模式应用场景 */ fn pattern_match_example() { // 1 匹配错误 match divide(3, 2) { Ok(result) => println!("Result: {}", result), Err(error) => println!("Error: {}", error), } } fn divide(a: i32, b: i32) -> Result<i32, String> { if b == 0 { return Err("Division by zero is not allowed".to_string()); } else { Ok(a / b) } }
-
解析命令行参数
-
解析配置文件
-
解析数据包
-
解析XML或JSON
高级匹配技巧
嵌套模式
这里 Message::Move { x, y }
是一个嵌套模式,因为它:
- 匹配外层枚举变体
Message::Move
- 同时解构了内部的结构体字段
{ x: i32, y: i32 }
这样你就可以在一个步骤中直接访问到 x
和 y
。
enum Message {
Quit,
Move { x: i32, y: i32 },
Write(String),
ChangeColor(i32, i32, i32),
}
let msg = Message::Move { x: 10, y: 20 };
match msg {
Message::Quit => println!("Quit"),
Message::Move { x, y } => println!("Move to ({}, {})", x, y),
Message::Write(text) => println!("Write: {}", text),
Message::ChangeColor(r, g, b) => println!("Change color to ({}, {}, {})", r, g, b),// 可以使用enum中的参数
}
匹配模式和迭代器:结合iter和match使用
for (a, b)
中的括号结构本质上是一种模式匹配语法- 它和
match
一样可以解构元组、结构体、枚举等复杂类型 iter()
提供了迭代器的能力,match
(或模式匹配)提供了结构解构的能力- 所以这段代码体现了
iter
和match
的结合使用
fn iterator_match_example() {
let vec1 = vec![1, 2, 3];
let vec2 = vec![4, 5, 6];
for (a, b) in vec1.iter().zip(vec2) {
println!("{} + {} = {}", a, b, a+b);
}
}
if let 和while let: 简化单个模式匹配
fn if_let_example() {
let option = Some(5);
if let Some(x) = option {
println!("{}", x);
}
}
fn while_let_example() {
let mut vec = vec![1, 2, 3];
while let Some(x) = vec.pop() {
println!("{}", x);
}
}
ref和ref mut
- 借用数据而不转移所有权:在某些情况下,你只需要借用数据而不是转移所有权。例如在递归数据结构中,借用数据可以避免所有权转移带来的复杂性
- 对数据进行修改:使用ref mut 可以在模式匹配时对对数据进行修改,而无需转移所有权
fn ref_example() {
let x = 5;
match x {
ref var => println!("{}", var),
_ => {}
}
}
fn ref_mut_example() {
let mut x = 5;
match x {
ref mut var => {
*var += 1;
}
_ => {}
}
}
练习
目标
- 理解如何使用 Rust 的模式匹配功能解析 JSON 数据。
- 学会使用
serde_json
库进行 JSON 处理。 - 练习在实际应用场景中使用模式匹配。
要求
- 使用
serde_json
库解析 JSON 字符串。 - 使用模式匹配提取 JSON 对象中的不同字段。
- 处理不同类型的数据(字符串、数字、数组、嵌套对象等)。
示例
假设你有一个包含用户信息的 JSON 字符串:
{
"name": "Alice",
"age": 30,
"email": "alice@example.com",
"address": {
"street": "123 Main St",
"city": "Wonderland"
},
"phone_numbers": ["123-456-7890", "987-654-3210"]
}
答案:
use serde_json::{Result, Value};
use std::fmt;
// 定义错误类型(替代简单的 String 错误)
#[derive(Debug)]
enum ParseError {
Json(serde_json::Error),
FieldMissing(&'static str),
TypeMismatch(&'static str, &'static str), // 字段名 + 期望类型
NumberRange(&'static str), // 数字超出范围
}
impl fmt::Display for ParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ParseError::Json(e) => write!(f, "JSON 解析错误: {e}"),
ParseError::FieldMissing(field) => write!(f, "字段缺失: {field}"),
ParseError::TypeMismatch(field, expect) => {
write!(f, "字段 {field} 类型错误,期望 {expect}")
}
ParseError::NumberRange(field) => write!(f, "字段 {field} 数值超出范围"),
}
}
}
impl From<serde_json::Error> for ParseError {
fn from(e: serde_json::Error) -> Self {
ParseError::Json(e)
}
}
#[derive(Debug)]
struct Address {
street: String,
city: String,
}
#[derive(Debug)]
struct Info {
name: String,
age: u32,
email: String,
address: Address,
phone_numbers: Vec<String>,
}
// 解析函数:使用模式匹配提取字段,返回 Result 处理错误
fn parse_json(json: &str) -> Result<Info, ParseError> {
let value: Value = serde_json::from_str(json)?;
// 1. 提取 name(字符串类型)
let name = match value.get("name") {
Some(Value::String(s)) => s.to_string(),
Some(_) => return Err(ParseError::TypeMismatch("name", "字符串")),
None => return Err(ParseError::FieldMissing("name")),
};
// 2. 提取 age(非负整数,且在 u32 范围内)
let age = match value.get("age") {
Some(Value::Number(n)) => {
n.as_u64()
.ok_or(ParseError::TypeMismatch("age", "非负整数"))?
.try_into()
.map_err(|_| ParseError::NumberRange("age"))?
}
Some(_) => return Err(ParseError::TypeMismatch("age", "数字")),
None => return Err(ParseError::FieldMissing("age")),
};
// 3. 提取 email(字符串类型)
let email = match value.get("email") {
Some(Value::String(s)) => s.to_string(),
Some(_) => return Err(ParseError::TypeMismatch("email", "字符串")),
None => return Err(ParseError::FieldMissing("email")),
};
// 4. 提取嵌套对象 address
let address = match value.get("address") {
Some(Value::Object(obj)) => {
let street = match obj.get("street") {
Some(Value::String(s)) => s.to_string(),
Some(_) => return Err(ParseError::TypeMismatch("address.street", "字符串")),
None => return Err(ParseError::FieldMissing("address.street")),
};
let city = match obj.get("city") {
Some(Value::String(s)) => s.to_string(),
Some(_) => return Err(ParseError::TypeMismatch("address.city", "字符串")),
None => return Err(ParseError::FieldMissing("address.city")),
};
Address { street, city }
}
Some(_) => return Err(ParseError::TypeMismatch("address", "对象")),
None => return Err(ParseError::FieldMissing("address")),
};
// 5. 提取数组 phone_numbers(元素为字符串)
let phone_numbers = match value.get("phone_numbers") {
Some(Value::Array(arr)) => {
arr.iter()
.map(|elem| match elem {
Value::String(s) => Ok(s.to_string()),
_ => Err(ParseError::TypeMismatch("phone_numbers 元素", "字符串")),
})
.collect::<Result<Vec<_>, _>>()?
}
Some(_) => return Err(ParseError::TypeMismatch("phone_numbers", "数组")),
None => return Err(ParseError::FieldMissing("phone_numbers")),
};
Ok(Info {
name,
age,
email,
address,
phone_numbers,
})
}
fn main() {
let json_str = r#"
{
"name": "Alice",
"age": 30,
"email": "alice@example.com",
"address": {
"street": "123 Main St",
"city": "Wonderland"
},
"phone_numbers": ["123-456-7890", "987-654-3210"]
}
"#;
match parse_json(json_str) {
Ok(info) => println!("解析结果:\n{:#?}", info),
Err(e) => eprintln!("解析失败: {e}"),
}
}
常见集合
Vec
基本用法
- 创建和初始化
- 添加元素
- 访问元素
- 修改元素
- 遍历元素
// 创建一个空的vec
let mut v: Vec<i32> = Vec::new();
// 使用宏来创建一个veck k
let mut v1: Vec<i32> = vec![1, 2, 3];
// 添加元素
v.push(5);
// 访问元素
// 1.使用索引
let third: &i32 = &v[2];
println!("The third element is {}", third);
// 2.使用get方法
match v.get(2) {
Some(third) => println!("The third element is {}", third),
None => println!("There is no third element."),
}
// 修改元素
v[0] = 4;
// 迭代元素
for i in &v {
println!("{}", i);
}
高阶用法:
// 进阶的用法
// 1.使用枚举来存储多种类型
enum SpreadsheetCell {
Int(i32),
Float(f64),
Text(String),
}
let row = vec![
SpreadsheetCell::Int(3),
SpreadsheetCell::Text(String::from("blue")),
SpreadsheetCell::Float(10.12),
];
// 2.容量与重新分配
let mut v = Vec::with_capacity(10);
v.push(1);
println!("{}", v.capacity());
常见错误:
-
不安全的索引访问,所以最好使用match
// 不安全的索引访问 let v2 = vec![1, 2, 3, 4, 5]; // println!("{}", v2[100]); // 运行时会出错
-
可变引用与不可变引用的混用
// 可变引用和不可变引用混用 let mut v3 = vec![1, 2, 3, 4, 5]; let first = &v3[0]; v3.push(6); // 这里会报错 println!("{}", first); --------------- cannot borrow `v3` as mutable because it is also borrowed as immutable mutable borrow occurs hererustcClick for full compiler diagnostic
修复的话可以分两种,可以修改push的顺序,或者使用作用域的特性来控制引用的生命周期
// 第一种 v3.push(6); let first: &i32 = &v3[0]; // 第二种 { let first: &i32 = &v3[0]; println!("{}", first); } v3.push(6);
HashMap
基本操作
// 基本操作
let mut scores: HashMap<String, i32> = HashMap::new();
scores.insert(String::from("Blue"), 10);
scores.insert(String::from("Yellow"), 50);
// 获取元素
let team_name = String::from("Blue");
let score: Option<&i32> = scores.get(&team_name);
match score {
Some(score) => println!("{}", score),
None => println!("None"),
}
// 遍历
for (key, value) in &scores {
println!("{} : {}", key, value);
}
进阶操作
-
更新哈希表
// 更新 scores.insert(String::from("Blue"), 25); scores.entry(String::from("Blue")).or_insert(50);
-
合并哈希表
// 合并两个集合 let mut map1 = HashMap::new(); map1.insert(1, "one"); let mut map2 = HashMap::new(); map2.insert(2, "two"); map2.insert(3, "three"); for (k, v) in &map2 { map1.entry(*k).or_insert(&v); } println!("{:?}", map1);
常见陷阱
-
哈希冲突
-
所有权问题
fn hashmap_ownership() {
let mut scores = HashMap::new();
let team_name = String::from("Blue");
let team_score = 10;
scores.insert(team_name, team_score);
println!("{:?}", scores);
println!("{}", team_name); // 这里会报错
修改为.clone()
scores.insert(team_name.clone(), team_score.clone());
练习
- 使用Vec实现一个简单的栈
struct Stack<T> {
elements: Vec<T>,
}
impl<T> Stack<T> {
// 初始化空栈
fn new() -> Self {
Stack {
elements: Vec::new(),
}
}
// 入栈:向尾部添加元素
fn push(&mut self, item: T) {
self.elements.push(item);
}
// 出栈:移除并返回尾部元素(空栈时返回 None)
fn pop(&mut self) -> Option<T> {
self.elements.pop()
}
// 查看栈顶:返回尾部元素的引用(空栈时返回 None)
fn peek(&self) -> Option<&T> {
self.elements.last()
}
}
#[test]
// 测试用例
fn main() {
let mut stack = Stack::new();
stack.push(1);
stack.push(2);
stack.push(3);
println!("栈顶元素(peek): {:?}", stack.peek()); // 输出: Some(3)
println!("出栈元素(pop): {:?}", stack.pop()); // 输出: Some(3)
println!("出栈后栈顶: {:?}", stack.peek()); // 输出: Some(2)
}
- 使用
HashMap
实现字频统计器
/**
* 通过hashmap计算单词频率
*/
fn count_frequency(s: &str) -> HashMap<&str, i32> {
let mut letters: HashMap<&str, i32> = HashMap::new();
for ch in s.split_whitespace() {
letters
.entry(ch)
.and_modify(|counter| *counter += 1)
.or_insert(1);
}
return letters;
}
#[test]
fn test_count_frequency() {
let s = "hello world hello rust";
let letters = count_frequency(s);
println!("{:?}", letters);
}
- 使用Vec和HashMap实现一个简单的书籍库存管理系统:
// 库存管理系统
struct InventorySystem {
books: Vec<Book>, // 存储所有书籍
id_to_index: HashMap<u32, usize>, // ID -> Vec索引的映射
next_id: u32, // 下一个可用ID(自增)
}
impl InventorySystem {
// 初始化空系统
fn new() -> Self {
InventorySystem {
books: Vec::new(),
id_to_index: HashMap::new(),
next_id: 1, // ID从1开始
}
}
// 添加书籍:自动分配ID,返回新书籍ID
fn add_book(&mut self, title: String, author: String, quantity: u32) -> u32 {
let id = self.next_id;
self.next_id += 1;
let book = Book {
id,
title,
author,
quantity,
};
let index = self.books.len();
self.books.push(book);
self.id_to_index.insert(id, index); // 记录ID与索引的映射
id
}
// 按ID查询书籍:返回Option<&Book>(不存在则返回None)
fn get_book(&self, id: u32) -> Option<&Book> {
self.id_to_index.get(&id).map(|&index| &self.books[index]) // 通过索引取书籍引用
}
// 按标题模糊查询:返回所有包含该标题的书籍
fn search_by_title(&self, title: &str) -> Vec<&Book> {
self.books
.iter()
.filter(|book| book.title.contains(title))
.collect()
}
// 更新库存:按ID修改数量,返回是否成功
fn update_quantity(&mut self, id: u32, new_quantity: u32) -> bool {
if let Some(&index) = self.id_to_index.get(&id) {
self.books[index].quantity = new_quantity;
true
} else {
false
}
}
// 删除书籍:按ID删除,返回是否成功
// (注:删除时将最后一本书移到被删位置,保证Vec索引一致性)
fn remove_book(&mut self, id: u32) -> bool {
if let Some(index) = self.id_to_index.remove(&id) {
// 若删除的不是最后一本书,将最后一本书移到删除位置
if index < self.books.len() - 1 {
let last_book = self.books.pop().unwrap(); // 取出最后一本书
self.books[index] = last_book.clone(); // 覆盖到删除位置
self.id_to_index.insert(last_book.id, index); // 更新最后一本书的索引映射
} else {
self.books.pop(); // 删除最后一本书,无需调整映射
}
true
} else {
false
}
}
}
// 测试用例
#[test]
fn test_inventory_system() {
let mut inventory = InventorySystem::new();
// 添加书籍
let id1 = inventory.add_book("Rust编程入门".to_string(), "张三".to_string(), 10);
let id2 = inventory.add_book("Effective Rust".to_string(), "李四".to_string(), 5);
// 查询书籍
println!("书籍1: {}", inventory.get_book(id1).unwrap());
println!("书籍2: {}", inventory.get_book(id2).unwrap());
// 更新库存
inventory.update_quantity(id1, 20);
println!("更新后书籍1: {}", inventory.get_book(id1).unwrap());
// 模糊查询
let results = inventory.search_by_title("Rust");
println!("含'Rust'的书籍:");
for book in results {
println!("{}", book);
}
// 删除书籍
inventory.remove_book(id2);
println!("删除书籍2后,查询是否存在: {:?}", inventory.get_book(id2));
}
评论
匿名评论隐私政策
✅ 你无需删除空行,直接评论以获取最佳展示效果