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

Rust基础-part8-模式匹配、常见集合

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

模式匹配

检查数据结构,提高代码的可读性和简洁性,减少错误,尤其在处理复杂数据结构的时候

基础模式匹配

fn match_example() {let x = 5;// 必须要有所有可能的casematch 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 }

这样你就可以在一个步骤中直接访问到 xy

  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(或模式匹配)提供了结构解构的能力
  • 所以这段代码体现了 itermatch 的结合使用
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. 提取嵌套对象 addresslet 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

基本用法
  • 创建和初始化
  • 添加元素
  • 访问元素
  • 修改元素
  • 遍历元素
// 创建一个空的veclet mut v: Vec<i32> = Vec::new();// 使用宏来创建一个veck klet 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());

练习

  1. 使用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)
}
  1. 使用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);
}
  1. 使用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,返回新书籍IDfn 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));
}
http://www.lryc.cn/news/603826.html

相关文章:

  • 亚马逊 Vine 计划:评论生态重构与合规运营策略
  • 学习笔记-中华心法问答系统的性能提升
  • 小孙学变频学习笔记(十二)机械特性的调整 机械特性的改善
  • 想要批量提取视频背景音乐?FFmpeg 和转换器都安排上
  • Day 25:异常处理
  • VTK开发笔记(一):VTK介绍,Qt5.9.3+VS2017x64+VTK8.2编译
  • Zynq SOC FPGA嵌入式裸机设计和开发教程自学笔记:GPIO扩展与中断控制技术,万字详解!!
  • 车载刷写架构 --- 整车刷写中为何增加了ECU 队列刷写策略?
  • 服务器分布式的作用都有什么?
  • Windows下基于 SenseVoice模型的本地语音转文字工具
  • Ubuntu25.04轻量虚拟机Multipass使用Shell脚本自动创建并启动不同版本Ubuntu并复制文件
  • 【支持Ubuntu22】Ambari3.0.0+Bigtop3.2.0——Step1—基础环境准备
  • GaussDB 约束的语法
  • 【LeetCode】前缀表相关算法
  • 服务器数据恢复—RAID上层部署的oracle数据库数据恢复案例
  • Node.js 是怎么一步步撼动PHP地位的
  • #C语言——学习攻略:深挖指针路线(三)--数组与指针的结合、冒泡排序
  • 云原生MySQL Operator开发实战(四):测试策略与生产部署
  • 什么情况下会出现数据库和缓存不一致的问题?
  • PowerShell脚本自动卸载SQL Server 2025和 SSMS
  • 传媒行业视频制作:物理服务器租用是隐藏的效率引擎
  • 基于Coze平台的自动化情报采集与处理引擎—实现小红书图文到飞书的端到端同步
  • MySQL数据库 mysql常用命令
  • 堆的理论知识
  • 青少年软件编程图形化Scratch等级考试试卷(一级)2025年6月
  • 人工智能赋能社会治理:深度解析与未来展望
  • 不靠海量数据,精准喂养大模型!上交Data Whisperer:免训练数据选择法,10%数据逼近全量效果
  • 光环云在2025WAIC联合发布“AI for SME 全球普惠发展倡议”
  • docker的安装和配置流程
  • 【监控】非IP监控系统改造IP监控系统