Rust基础-part8-模式匹配、常见集合
Rust基础[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 immutablemutable 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(不存在则返回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));}