Rust匹配机制探讨

写在前面

前面我们介绍了rust的基础语法和所有权机制,接下来我们来看看匹配机制,相信在前面是示例中你已经看到过match关键字
match 是 Rust 中的模式匹配表达式,类似于其他语言中的 switch-case,但功能强大得多。它允许你:

  • 基于值执行不同的代码路径

  • 解构复杂数据结构

  • 确保处理所有可能情况

  • 简洁清晰地表达程序逻辑

基本match语法

match表达式由多个分支(arms)组成。每个分支由一个模式(pattern)和关联的代码块组成,模式匹配成功则执行对应的代码块, 来看看简单示例:

match value {
     pattern1 => expression1, 
     pattern2 => { // 多行代码块
     },
     _ => expression3, // 默认情况
}

例如来匹配简单数字,看示例:

fn describe_number(n: i32) -> String {
     match n { 1 => "一".to_string(),
     2 => "二".to_string(),
     3 => "三".to_string(),
     4..=10 => "四到十之间的数".to_string(),
     _ => "其他数字".to_string(),
 }}

fn main() {
     println!("5 是:{}", describe_number(5)); // 输出:5 是:四到十之间的数
}

_ 通配符表示匹配任何值。在 Rust 中,使用 match 时必须处理所有可能情况,_ 确保了这个要求

搭配枚举

match搭配上枚举将是非常厉害的工具,来看看示例

enum WebEvent {
     PageLoad, PageUnload, KeyPress(char), Paste(String), Click { x: i32, y: i32 },
}

fn inspect(event: WebEvent) -> String {
     match event { WebEvent::PageLoad => "页面加载".to_string(),
     WebEvent::PageUnload => "页面卸载".to_string(),
     WebEvent::KeyPress(c) => format!("按下了字符: {}", c),
     WebEvent::Paste(s) => format!("粘贴了文本: {}", s),
     WebEvent::Click { x, y } => format!("点击了坐标 ({}, {})", x, y), }}

fn main() {
     let click = WebEvent::Click { x: 100, y: 200 }; println!("{}", inspect(click)); // 输出:点击了坐标 (100, 200)}

再看一下示例:

struct Rectangle {
     width: u32, height: u32
}

enum Shape {
     Rectangle(Rectangle), Triangle((u32, u32), (u32, u32), (u32, u32)), Circle { origin: (u32, u32), radius: u32 },
}

fn shape_match(shape: Shape) {
     match shape { Shape::Rectangle(a_rec) => {  // 解出一个结构体
     println!("Rectangle {}, {}", a_rec.width, a_rec.height); } Shape::Triangle(x, y, z) => {  // 解出一个元组
     println!("Triangle {:?}, {:?}, {:?}", x, y, z); } Shape::Circle {origin, radius} => {  // 解出一个结构体的字段
     println!("Circle {:?}, {:?}", origin, radius); } }
}

fn main() {
     let rectangle = Shape::Rectangle(Rectangle { width: 10, height: 20, });
     let triangle = Shape::Triangle((0, 1), (3,4), (3, 0));
     let circle = Shape::Circle { origin: (0, 0), radius: 5 };
     shape_match(rectangle); // Rectangle 10, 20 
     shape_match(triangle);  // Triangle (0, 1), (3, 4), (3, 0) 
     shape_match(circle);    // Circle (0, 0), 5}
}     

Option

Option:处理”有值”或”无值”,这个类型在日常开发中需要经常使用,大部分都用来作为函数或者方法的返回值,然后在match中进行解构,本质还是匹配+枚举,这个类型其实就是定义在标准库中的一个枚举类型,源码如下:

enum Option<T> {
 Some(T), None,}

我们来看示例:

fn increment(number: Option<i32>) -> Option<i32> {
     match number { None => None, Some(i) => Some(i + 1), }}

fn main() {
     let five = Some(5); let none = None; println!("Some(5)+1 = {:?}", increment(five)); // 输出:Some(6)
     println!("None+1 = {:?}", increment(none));   // 输出:None
}

我们知道的其他语言中是有一个叫nil值或者null值的,但是rust设计者是没有做这种东西的,但是我们可以使用匹配机制来实现类似的效果,看示例:

fn safe_divide(numerator: f64, denominator: f64) -> Option<f64> {
     match denominator { 0.0 => None, d => Some(numerator / d), }}

fn main() {
     let result = safe_divide(10.0, 0.0); match result { Some(value) => println!("结果: {}", value),
     None => println!("错误: 除数为零"),
 }}

为了方便理解,这里写一个go的示例:

package main

import (
 "errors" "fmt")

func safeDivide(numerator, denominator float64) (float64, error) {
     if denominator == 0 { 
         return 0, errors.New("除数为零")
     } 
     return numerator / denominator, nil
}

func main() {
 res, err := safeDivide(10, 0) 
 if err != nil { 
     fmt.Println("err:", err) 
    return 
  }
 fmt.Println("res:", res)
}

输出:

err: 除数为零

Result<T, E>

Result 是 Rust 中表示成功或错误的类型,主要用于函数或者方法的返回值,这么看和go挺像的是吧,在标准库的定义:

enum Result<T, E> {
 Ok(T), Err(E),}

来看一个操作文件的示例:

use std::fs::File; // 引入标准库的fs:File
use std::io::Read; // 引入标准库的io::Read

fn read_file_contents(path: &str) -> Result<String, std::io::Error> { // 返回Result<T, E>
     let mut file = match File::open(path) { // 打开成功后将其文件句柄绑定给file, 失败直接返回Err并且携带错误信息
     Ok(f) => f, Err(e) => return Err(e), };     let mut contents = String::new();
     match file.read_to_string(&mut contents) { // 将文件读取给contents读取成功者返回contents, 失败则返回Err并且携带错误信息
     Ok(_) => Ok(contents), Err(e) => Err(e), }
}

fn main() {
         match read_file_contents("hello.txt") { Ok(contents) => println!("文件内容: {}", contents),
         Err(e) => println!("读取文件出错: {}", e),
    }
 }

当然,这里还有更直接的解构方法:

 // 如果想使一个可恢复错误按不可恢复错误处理,Result 类提供了两个办法:unwrap() 和 expect(message: &str) :
 let f4 = File::open("hello.txt").unwrap(); let f5 = File::open("hello.txt").expect("Failed to open.");

其实在外面的main函数都是有返回值的,比如说这个示例:

use std::io::prelude::*;
use std::fs::OpenOptions;

fn main() -> std::io::Result<()> {

 let mut file = OpenOptions::new() .append(true).open("text.txt")?;
     // 在尾部追加
     file.write(b" APPEND WORD")?;
     // 返回()
     Ok(())
 }

大部分情况下我们没有写main的返回值,但他确实有返回一个Result<()>, 这个点可以留意一下

高级用法

| 匹配多个模式

fn is_vowel(c: char) -> bool {
     match c.to_ascii_lowercase() { 'a' | 'e' | 'i' | 'o' | 'u' => true, _ => false, }}

fn main() {
     println!("'a' 是元音吗? {}", is_vowel('a')); // true
     println!("'b' 是元音吗? {}", is_vowel('b')); // false
}

嵌套匹配

enum Shape {
     Circle(f64), Rectangle { width: f64, height: f64 },}

enum Color {
     Red, Green, Blue, Rgb(u8, u8, u8),}

struct ColoredShape {
     shape: Shape, color: Color,}

fn describe_shape(colored_shape: ColoredShape) -> String {
     match colored_shape { ColoredShape { shape: Shape::Circle(radius), color: Color::Red, } => format!("红色圆形 (半径: {})", radius),
         ColoredShape {
 shape: Shape::Rectangle { width, height }, color: Color::Rgb(r, g, b), } => format!("RGB({},{},{})颜色的矩形 (宽: {}, 高: {})", r, g, b, width, height),
         ColoredShape { shape, color } => format!("形状: {:?}, 颜色: {:?}", shape, color),
 }}

绑定到变量@

@ 运算符允许在检查模式的同时绑定值到变量:

fn process_age(age: u8) -> String {
 match age { 0 => "刚出生".to_string(),
     1..=3 => "婴儿".to_string(),
     4..=12 => "儿童".to_string(),
     // 绑定特定值到变量
     teenager @ 13..=19 => format!("青少年({}岁)", teenager),
     // 绑定范围到变量
     a @ 20..=100 => format!("成年人({}岁)", a),
     _ => "特别长寿的人!".to_string(),
  }
 }

匹配守卫:添加额外条件

匹配守卫允许在模式后面添加条件表达式:

fn check_point(point: (i32, i32)) -> String {
 match point { (x, y) if x == 0 && y == 0 => "原点".to_string(),
     (x, _) if x == 0 => "在Y轴上".to_string(),
     (_, y) if y == 0 => "在X轴上".to_string(),
     (x, y) if x > 0 && y > 0 => "第一象限".to_string(),
     (x, y) if x < 0 && y > 0 => "第二象限".to_string(),
     (x, y) if x < 0 && y < 0 => "第三象限".to_string(),
     (x, y) if x > 0 && y < 0 => "第四象限".to_string(),
     _ => "未知位置".to_string(),
  }
 }

使用 if let 简化简单匹配

fn main() {
     let some_value = Some(5);
     // 传统 match match some_value { Some(x) => println!("值为: {}", x),
     None => (), // 什么都不做
     }
     // 使用 if let 更简洁
     if let Some(x) = some_value { println!("值为: {}", x);}
 }

使用 while let 处理迭代

while let 可以简化特定模式的循环, 解释一下这里的原理,本质就是循环匹配,当stack的数据为空后,自然就

let mut stack = vec![1, 2, 3];

println!("栈内容:");
while let Some(top) = stack.pop() {
     println!("{}", top);}
// 输出:
// 3
// 2
// 1

简单实践

最后我们来看一个这个简单示例:

fn main() {
    // 1. 定义简单会话类型
    struct Session {
        db_index: u8,
    }

    // 2. 创建会话映射表
    let mut sessions = std::collections::HashMap::new();
    sessions.insert(1, Session { db_index: 0 });
    sessions.insert(2, Session { db_index: 1 });

    // 3. 要查询的会话ID
    let session_id = 1;

    // 4. 根据会话获取db_index
    let db_index = {
        if let Some(session) = sessions.get(&session_id) {
            session.db_index
        } else {
            println!("Session not found!");
            return;
        }
    };

    println!("Session {} using database: {}", session_id, db_index);
}
本作品采用《CC 协议》,转载必须注明作者和本文链接
刻意学习
讨论数量: 0
(= ̄ω ̄=)··· 暂无内容!

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!
未填写
文章
128
粉丝
107
喜欢
197
收藏
283
排名:331
访问:2.8 万
私信
所有博文
社区赞助商