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

Rust模式匹配详解

模式在Rust中主要有两个用途

  • 实现数据的模式匹配和解构,先看看这个数据长什么样子(是否符合某个结构),如果符合,就把它“拆开”,把里面的各个部分拿出来单独使用
  • 程序流程控制,match、while、if等都可以根据模式是否与值匹配来决定程序的执行路径。
    模式匹配可以用于标量值,也可以用于符合数据类型(结构体、枚举、数组等)

模式由一系列规则和符号构成,由于描述一个值的结构。模式匹配可用于变量绑定、表达式、函数参数、返回值等场合。

let语句

let通过模式匹配机制将一个值绑定到新变量上,如果模式就是单个变量名的形式,那么它就属于不可反驳模式,即不存在匹配失败的情况。

fn main() {let a = 3;let b = (1,3);println!("{a}");println!("{b:?}")
}

以下是更复杂的例子

匹配元组

fn main() {let (a,b) = (1,2);println!("{a} {b}");
}

匹配数组

fn main() {let list = [1, 2, 3, 4];let [a, b, c, d] = list;println!("{} {} {} {}", a, b, c, d);
}

对现有变量进行模式匹配

fn main() {let mut a = 0;let mut b = 0;(a, b) = (1, 2);println!("{} {}", a, b)
}

通配符

下划线(_)用于忽略某个对应的值

fn main() {let list = [1, 2, 3, 4];let [a, _, _, d] = list;println!("{a} {d}")
}

双点号(..)用来忽略一部分连续值

fn main() {let list = [1, 2, 3, 4];let [a, .., d] = list;println!("{a} {d}")
}

复杂模式

可以使用更复杂的模式来对复合数据类型的值进行完全解构。

fn main() {let data = ((1, 2), (3, 4));let (a, b) = data;println!("{:?} {:?}", a, b)
}

我们还可以进一步对元组进行解构

fn main() {let data = ((1, 2), (3, 4));let ((a,b), (c,d)) = data;println!("{} {} {} {}", a, b, c, d);
}

使用模式移除引用语义

fn main() {let ref1 = &13;let (&data) = ref1;println!("{}", ref1);
}

所有权

在使用模式解构时,所有权可能会发生转移。

  • 对于实现了Copy语义的类型,所有权不会转移
  • 对于实现了Clone语义的类型,解构操作会导致所有权被转移给新的变量绑定。
fn main() {let tuple = ("Bob".to_string(), 13);let (a,b) = tuple;println!("{}", tuple.0); //会报错println!("{}", tuple.1);println!("{a} {b}");
}

由于字符串支持Clone语义,而整数实现了Copy语义,因此tuple.0的所有权被转移了,而tuple.1的所有权没被转移。
解决办法是使用ref关键字,它会在模式匹配时创建对该值的引用

fn main() {let tuple = ("Bob".to_string(), 13);let (ref a,b) = tuple;println!("{}", tuple.0); println!("{}", tuple.1);println!("{a} {b}");
}

mut关键字可以出现在模式中,在解构一个值时,默认是不可变的,但可以用mut关键字声明可变的绑定。

fn main() {let (mut a, b) = ("bob".to_string(), 13);a = "joe".to_string();println!("{a}");
}

可以在模式中组合ref和mut来声明一个可变引用。此时Rust的可变性规则(同一时间只能存在一个可变引用)依然适用

fn main() {let mut tuple = ("bob".to_string(), 13);let (ref mut a, b) = tuple;a.push('!');println!("{}",tuple.0);println!("{}",tuple.1);
}

当一个值没有实现Copy语义时,通过解构就会发生所有权转移。但是,如果在模式中使用_忽略该值,所有权就不会发生转移。

fn main() {let tuple = ("bob".to_string(), "def".to_string());let (_, b) = tuple;println!("{}",tuple.0);println!("{}",tuple.1); //报错了
}

但是,当我们忽略模式绑定到的变量_variable_name时,情况就不一样了。

fn main() {let tuple = ("bob".to_string(), "def".to_string());let (_, _b) = tuple;println!("{}",tuple.0); // 被忽略了(未移动)println!("{}",tuple.1); // 被绑定了(被移动)
}
  • 由于tuple.0被忽略了(_),没有进行任何绑定,所以所有权没有转移;
  • 对于tuple.1,虽然后面使用_b进行了绑定,而且_b并没有使用,但tuple.1的值仍然被绑定到_b上,所有权发生了转移。

不可反驳模式

在Rust中,模式分为可反驳和不可反驳两种。

  • 不可反驳是全面的,可以匹配任何情况,因此始终会匹配成功
  • 可反驳只能匹配一部分特定的表达式,也可能完全不匹配,因此你必须提供另一条控制分支以处理无法匹配的情况。
    let语句只接受不可反驳模式
fn main() {let a = 10;println!("{a}");
}

试图用let语句处理可反驳模式是行不通的。

fn main() {let option = Some(12);let Some(value) = option;println!("{}",value);
}

let语句本身没有为匹配失败提供可替代方案。如果匹配失败,就必须提供后备计划,以指导编译器正确地编译。

范围模式

范围模式(模式中使用范围)属于可反驳模式。范围模式只适用于数值类型和字符类型。

  • begin..=end -> 表示[begin, end]这个范围
  • begin.. -> 表示[begin, 最大值]这个范围
  • ..=end -> 表示[最小值, end]这个范围

示例

fn main() {let tuple = get_value();if let (1..=10, 1..=10) = tuple {println!("匹配成功")} else {println!("匹配失败")}
}fn get_value()->(i8, i8) {(10, 10)
}

前面在匹配成功时只输出一条信息,如果能知道匹配的具体值就好了,使用@语法可以将变量绑定到模式范围

binding@range

示例

fn main() {let tuple = get_value();if let (a@1..=10, b@1..=10) = tuple {println!("匹配成功({a}, {b})")} else {println!("匹配失败")}
}fn get_value()->(i8, i8) {(10, 10)
}

多个模式

你可以使用管道符(|)将多个模式组合起来

fn main() {let value = get_value();if let 10|12|30 = value {println!("匹配成功");} else {println!("匹配失败");}
}fn get_value()->i8 {12
}

在复合模式中匹配一个值,想要知道匹配的具体是哪个值,我们同样可以使用@

fn main() {let value = get_value();if let a@(10|12|30) = value {println!("匹配成功 {a}");} else {println!("匹配失败");}
}fn get_value()->i8 {12
}

控制流

if-let-else

前面我们使用了if-let-else这样的形式。这里的模式是可反驳的。

if let pattern=expression {//匹配成功的代码块
} else {// 不匹配的代码块(可选的)
}

示例

fn main() {let value = get_value();if let Some(v) = value {println!("匹配的值为{}",v)}
}fn get_value()->Option<i8> {Some(23)
}

while-let

也可以在while let表达式中使用模式。

  • 只要模式匹配到值,while循环就会一直执行,直到模式不能匹配到值时终止。
  • 通过模式匹配成功的值,只在while循环体内有效
fn main() {let vec = vec![1,2,3,4,5];let mut i = 0;while let Some(item) = vec.get(i) {println!("{item}");i+=1;}
}

for-in

for表达式的实现基于迭代器和模式匹配机制,在每次迭代时,它会调用next方法。

  • 只要next返回Some(item),迭代就会继续执行
  • 一旦返回None,迭代器就会终止。
fn main() {let data = [1,2,3,4,5];for (index, item) in data.iter().enumerate() {println!("index {index}; item {item}")}
}

结构体

模式匹配也用于结构体,对结构体进行解构赋值非常有用。
对于结构体模式,至少包含结构体名称和正确的字段名(字段顺序无关紧要),在默认情况下,结构体字段会被绑定到同名变量上。

struct Rectangle {p1: (u32, u32),p2: (u32, u32),
}
fn main() {let r = Rectangle {p1: (1, 2),p2: (3, 4),};let Rectangle {p1, p2} = r; //解构println!("{:?} {:?}", p1, p2);
}

我们还可以进一步解构

struct Rectangle {p1: (u32, u32),p2: (u32, u32),
}
fn main() {let r = Rectangle {p1: (1, 2),p2: (3, 4),};let Rectangle {p1: (a,b), p2:(c, d)} = r; //解构println!("{a} {b} {c} {d}");
}

在解构结构体时,还可以通过field_name:binding_name规则进行重命名

struct Rectangle {p1: (u32, u32),p2: (u32, u32),
}
fn main() {let r = Rectangle {p1: (1, 2),p2: (3, 4),};let Rectangle {p1: top_left, p2:botton_right} = r; //解构println!("p1: {:?}; p2: {:?}", top_left, botton_right);
}

字面量对模式进行了细化,只有满足字面量的值的模式才能匹配。通过这种方式为模式匹配添加额外条件。由于else分支的存在,该模式是可反驳的。

struct Rectangle {p1: (u32, u32),p2: (u32, u32),
}
fn main() {let r = Rectangle {p1: (1, 2),p2: (3, 4),};if let Rectangle {p1: top_left, p2:(3, 4)} = r {println!("p1: {:?}", top_left);} //解构
}

你也可以在解构模式时使用(_)通配符来忽略一个字段值。使用(…)通配符来忽略任何剩余的值。

函数

模式匹配也可以用在函数参数上,不过只能使用不可反驳模式。

fn do_something((x, y): (u8,u8)) {println!("{x} {y}");
}fn test() {do_something((1,2));do_something((2,3));do_something((3,4));
}
fn main() {test();
}

match表达式

match表达式与模式的结合被广泛用于控制流,match的每个分支左侧对应一个模式,当被测试的值与该模式匹配时,该分支就会被执行。
匹配的模式必须穷尽所有可能的情况,如果没有,就需要添加一个默认模式(_)来匹配默认情况。因为默认模式是不可反驳的,所有它应该作为最后一个分支。

fn main() {let value = get_value();match value {1=>println!("One"),2=>println!("Two"),_=>println!("Unknown"),}
}
fn get_value()->i8 {12
}
http://www.dtcms.com/a/434252.html

相关文章:

  • 石家庄做网站建设公司安徽省建设厅网站职称申报
  • gitlab-runner 再次实践中理解和学习
  • C++之stack等容器适配器(上)实战篇
  • JavaWeb零基础学习Day1——HTMLCSS
  • Starting again-01
  • 如何做网站链接使用朝阳seo建站
  • Nivo 用React打造精美数据可视化的开源利器
  • 【iOS安全】iPhone X iOS 16.7.11 (20H360) Palera1n MacOS版 越狱教程
  • 【连载4】数据库热点更新场景调优策略
  • 深入解析HarmonyOS ArkTS:从语法特性到实战应用
  • 小杰深度学习(five)——正则化、神经网络的过拟合解决方案
  • 网页网站开发大概多少钱网站设计公司 深圳龙华
  • 门户网站是什么意思?网站建设哈尔滨app开发2
  • 《吃透 C++ vector:从基础使用到核心接口实战指南》
  • wordpress上传到哪里什么网站利于优化
  • [Web网页] 零基础入门 HTML
  • Nimble:让SwiftObjective-C测试变得更优雅的匹配库
  • HTML——1px问题
  • 【C++项目】基于微服务的即使通信系统
  • wordpress企业站模板建协官网
  • 【办公类-115-04】20250920职称资料上传03——压缩课题结题报告PDF的大小(控制在200MB以内)
  • Python - 100天从新手到大师:第二十七天Python操作PDF文件
  • 做网站一般长宽多少如何线上推广引流
  • C++20 协程:在 AI 推理引擎中的深度应用
  • 如何做游戏推广seo整站优化
  • CompLLM 来了:长文本 QA 效率革命,线性复杂度 + 缓存复用,推理速度与效果双丰收
  • 嵌入式开发中用于调试的技术Semihosting
  • 企业营销型网站建设哪家好手机网站制作 尺寸
  • Python 3 与 MongoDB 的深入探索
  • toLua[五] Examples 04_AccessingLuaVariables分析