概述

模式匹配是一种机制,用于判断某个类型或值是否匹配某一个模式,一旦匹配,就可以根据模式中指定的规则对类型或值进行解析。模式匹配经常应用于数据结构字符串中,比如在某个字符串中查找特定字串。在Rust中,模式匹配已经成为语言设计的关键特性之一,并可以应用于更加广泛的数据类型中。

模式

模式(PATTERN)是 Rust 中特殊的语法,它用来匹配类型的结构,无论类型是简单还是复杂。结合使用模式和match表达式以及其它结构可以提供更多对程序控制流的支配权。模式通常由以下一些内容组合而成:

  • 字面值;
  • 解构的数组、枚举、结构体或者元组;
  • 变量;
  • 通配符;
  • 占位符。

这些部分描述了我们要处理的数据的形式,接着可以用其匹配值来决定程序是否拥有正确的数据来运行特定部分的代码。

模式使用的位置

模式在Rust语言中的应用广泛,除了最常见match语句外,包括let、if let、while let、for语句以及函数参数中都有对模式的使用。

let

不同于其它语言,Rust使用let定义变量的本质也是在使用模式匹配,在Rust中,使用let语句的正式语法形式其实是下面这个样子:

let PATTERN = EXPRESSION;

考虑下面一个简单的let变量定义:

let x = 5;

这里变量名实际就是一种形式简单的模式,所以语句let x = 5;中,x是一个模式代表“将匹配到的值绑定到变量x”。

if let

在if语句中,可以将let作为判断条件,如:

    let x = Some(5);if let Some(val) = x {println!("val = {}", val);}

while let

while let允许在while循环中连续使用模式匹配,匹配失败时会退出循环,使用示例如下:

let mut stack = vec![1, 2, 3];while let Some(top) = stack.pop() {println!("{}", top);
}

match

Rust的match类似于C语言中的switch,但提供了更强大的流程控制能力,它允许我们将一个值与一系列的模式相比较,并根据相匹配的模式执行相应的代码。match的语法形式如下:

match VALUE {PATTERN => EXPRESSION,PATTERN => EXPRESSION,PATTERN => EXPRESSION,
}

for

在 for 循环中,模式是 for 关键字直接跟随的值,正如for x in y中的x。下面示例展示了在for循环中使用模式来解构元组:

let v = vec!['a', 'b', 'c'];for (index, value) in v.iter().enumerate() {println!("{} is at index {}", value, index);
}

模式的Refutability( 可反驳性)

根据模式是否会匹配失效,Rust模式可以分为两种两种形式: refutable( 可反驳的)和 irrefutable( 不可反驳的),其中:

  • 能匹配任何传递的可能值的模式被称为是不可反驳的( irrefutable),如let x = 5,语句中的模式x可以匹配任何值,所以不可能失败;
  • 对某些可能的值进行匹配会失败的模式被称为是可反驳的(refutable)。一个这样的例子便是if let Some(x) = a_value表达式中的 Some(x) ;如果变量a_value中的值是None而不是Some,那么Some(x) 模式不能匹配。

函数参数、 let 语句和 for 循环只能接受不可反驳的模式, 因为通过不匹配的值程序无法进行有意义的工作;if let 和 while let 表达式被限制为只能接受可反驳的模式, 因为根据定义他们意在处理可能的失败: 条件表达式的功能就是根据成功或失败执行不同的操作。

模式语法

Rust支持的模式语法类型见下表:

接下来会介绍日常开发常用的一些模式。

模式中的字面量、变量与通配符

字面量、变量等都可以作为模式在Rust中使用:

match get_count() {     //  get_count返回整型数值0 => println!("res is 1."),1 => println!("res is 1."),count => println!("res is {}", count),
}

这里0、1等整数值都是作为模式在使用;对于变量count,可以匹配其它任意值并保存。很重要的一条规则:模式匹配的值会被复制或转移到模式内定义的局部变量(如果有)中,具体是复制还是转移则取决于匹配的值是否是可复制类型(实现了Copy trait)。

通配符_

关于通配符_,类似于C语言switch语句中default分支,用于匹配不与任何其它模式匹配的值:

match get_count() {     //  get_count返回整型数值0 => println!("res is 0."),1 => println!("res is 1."),_ => {},    // 前面语句无法匹配,通通走这条分支进行处理
}

结构体模式

结构体模式用于匹配结构体,其中每个字段都是一个子模式:

struct Point {x: i32,y: i32,
}let point = Point {x: 5, y: 6};
match point {Point {x: x, y: y} => println!("Point at {},{}", x, y),
}

匹配结构体的每个字段都会被拷贝到新的局部变量中。对于复杂的结构体类型,如果只想匹配其中几个字段,可以使用..省略其它字段:

match point {Point {x: x, ..} => println!("x is {}", x),
}

引用模式

对于引用,Rust支持两种模式:ref模式和&模式,其中,ref模式匹配值并借用匹配值的引用;&模式匹配引用。下面的示例较为直观地展示了ref和&在使用上的差异:

    let mut a: i32 = 1;// ref匹配值let ref a_ref: i32 = a;     // 借用a的不可变引用let ref mut a_mref: i32 = a;        // 借用a的可变引用// &匹配引用let &b: &i32 = &a;      // 匹配不可变引用,b为a的副本let &mut b: &mut i32 = &mut a;    // 匹配可变引用,b为a的副本

下面针对ref和&的使用更详细地进行说明。

ref模式

Rust使用模式匹配不可复制的值会转移值,考虑下面的代码:

struct Account {name: String,language: String,
}match account {Account{name, language} => {println!("account: {}, {}", name, language);println!("account: {}, {}", account.name, account.language);        // 错误:使用转移的值account}
}

这里,由于account.name和account.language都是不可复制的,因而匹配会导致其值被转移到局部变量name和language中,之后account也就不能再使用了。这种情况下,如果只想借用account的成员值,就可以使用ref模式。ref模式可以实现借用而不转移匹配的值。

match account {Account{ref name, ref language} => {println!("account: {}, {}", name, language);println!("account: {}, {}", account.name, account.language);        // account仍然可以正常使用}
}

这里,局部变量name和language存储的是对account中对应字段的不可变引用。如果希望借用可变引用,可以使用ref mut。

&模式

以&开头的模式匹配引用,以下面代码举例说明:

struct Point {x: i32,y: i32,
}Point point = Point {x: 5, y: 6};match &point {&Point {x, y} => println!("point: {}, {}", x, y),
}

表达式和模式天生是相反的,例如表达式(x, y)用两个值创建一个新元组,模式(x,y)则相反:它匹配元组并将其破坏后取出两个值。对&而言:表达式的&创建引用,模式中的&匹配引用。匹配引用时,不能对不可变引用采取mut操作,也不能从引用(包括mut引用)中转移出值,如果对包含不可复制字段的结构体引用进行匹配则会导致错误。

相关参考

  • 《Rust程序设计》
  • 《Rust程序设计语言》

Rust语言之模式匹配相关推荐

  1. 想要改变世界的 Rust 语言

    一门编程语言就像一个小宇宙,语言中的各种语法概念就像一颗颗星辰.对于初学者来说,看这些语法概念与看星罗棋布时产生的迷惑是相似的.幸亏编程语言是由人类创造的,编程语言的作者可以被找到,编程语言的源码也可 ...

  2. rust美服为什么给我半了_半个月使用rust语言的体验

    从第一次下载rust语言的编译器到今天刚好第14天. 简单说一下对这个语言的感觉吧. 一.性能 把以前用java写的一个中文地址切分的算法,用rust重新实现了一下(https://github.co ...

  3. rust木炭有用吗_半个月使用rust语言的体验

    从第一次下载rust语言的编译器到今天刚好第14天. 简单说一下对这个语言的感觉吧. 一.性能 把以前用java写的一个中文地址切分的算法,用rust重新实现了一下(https://github.co ...

  4. 【一天一门编程语言】Rust 语言程序设计极简教程

    文章目录 Rust 语言程序设计极简教程 介绍 安装 Rust Hello, World 基础语法 变量及数据类型 控制结构 `if` 语句 `while` 语句 `for` 语句 函数 泛型 泛型的 ...

  5. Rust语言的成长经历

    文章目录-Rust政治方式解读 是什么 为什么 怎么办 是什么 在中学时代,我们政治老师经常跟我们讲,解好"问答题"类型题目,分三步走: 1,是什么? 2,为什么? 3,怎么办? ...

  6. Rust语言入门(2)——设计哲学

    设计哲学 1 简述 任何一门语言的兴起,都是为了解决一个问题. 自操作系统诞生以来,系统级主流变成语言,从汇编语言到C++, 已经发展了近50年.但仍然存在两个难题: 很难编写内存安全的代码 很难编写 ...

  7. 五分钟入门rust语言

    1. 入坑rust 1.1 rust发展历程 2006年,Mozilla 员工 "Graydon Hoare" 开发了Rust. 2015年5月15日,Rust编程语言核心团队正式 ...

  8. rust语言流程控制

    流程控制 rust代码是从上至下顺序执行的,在这个过程中,可以通过循环,分支等流程控制方式来实现相应的逻辑. if-else rust的if-else和其它语言中的类似,但是if-else在rust中 ...

  9. Rust太难?那是你没看到这套Rust语言学习万字指南!

    摘要:从开发环境.语法.属性.内存管理和Unicode等五部分,为你带来一份详细的Rust语言学习的精华总结内容. 一.Rust开发环境指南 1.1 Rust代码执行 根据编译原理知识,编译器不是直接 ...

最新文章

  1. 【综述专栏】关于AI Architecture未来的一些思考
  2. poj3624 Charm Bracelet DP 01背包问题
  3. 深度学总结:CNN Decoder, Upsampling的处理
  4. mysql导入的时候提示“1046-No Database selected”的解决办法
  5. proxifier代理失败原因_上海财务代理
  6. es6 --- Promise封装读取文件操作
  7. 最长等差数列_最长等差数列分析
  8. 命令行下使用curl,采集数据遇到的问题。
  9. dnslog盲注原理
  10. Linux中的shell正则表达式详解
  11. 数据预处理-数据变换-小波变换
  12. anaconda安装sklearn_1. Sklearn —— 简介+安装
  13. 做Java软件开发有前途吗?
  14. 环境微生物复习题及答案
  15. html中省略号怎么打,Web中的省略号
  16. Spring常用注解介绍 [附带代码]
  17. python语言的实验心得体会范文_关于实验的心得体会范文5篇
  18. OpenCV简单图像分割
  19. Acwing:我在哪(二分+字符串哈希 Python)
  20. SQL日期与时间类型

热门文章

  1. 2.7通用串行总线 USB Universal Serial Bus
  2. matlab 与latex结合,TeX系列: MATLAB和LaTeX结合绘图
  3. 电气间隙Vs爬电距离
  4. Devops学习路线图
  5. 华为又开始放大招了?CV新架构:VanillaNet: the Power of Minimalism in Deep Learning 论文阅读笔记
  6. “嫦娥四号”探测器今年着陆月球背面
  7. Web前端 VS Web后端
  8. python模拟浏览器上传文件_Python模拟浏览器上传文件脚本的方法(Multipart/form-data格式)...
  9. oracle数据库计算时间差,Oracle计算时间差常用函数
  10. c语言汉字的存放和输出,怎么在C语言里用printf输出一个中文