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 协议》,转载必须注明作者和本文链接
推荐文章: