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

玩转Rust高级应用 如何进行理解Refutability(可反驳性): 模式是否会匹配失效

所有可能会用到模式的位置

模式出现在 Rust 的很多地方。你已经在不经意间使用了很多模式!本节将介绍所有模式有效的位置。

match 分支

如第六章所讨论的,一个模式常用的位置是 match 表达式的分支。在形式上 match 表达式由 match 关键字、用于匹配的值和一个或多个分支构成,这些分支包含一个模式和在值匹配分支的模式时运行的表达式,如下所示:

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

例如这是一个来自示例 6-5 中匹配变量 xOption<i32> 值的 match 表达式:

match x {None => None,Some(i) => Some(i + 1),
}

这个 match 表达式中的模式为每个箭头左边的 NoneSome(i)

match 表达式的一个要求是它们必须是穷尽exhaustive)的,意为 match 表达式所有可能的值都必须被考虑到。一个确保覆盖每个可能值的方法是在最后一个分支使用捕获所有的模式:比如,一个匹配任何值的名称永远也不会失败,因此可以覆盖所有匹配剩下的情况。

有一个特定的模式 _ 可以匹配所有情况,不过它从不绑定任何变量。例如这在希望忽略任何未指定值的情况很有用。本章之后的 [“忽略模式中的值”][ignoring-values-in-a-pattern] 部分会详细介绍 _ 模式的更多细节。

if let 条件表达式

第六章讨论过了 if let 表达式,以及它是如何主要用于编写等同于只关心一个情况的 match 语句简写的。if let 可以对应一个可选的带有代码的 elseif let 中的模式不匹配时运行。

示例 19-1 展示了也可以组合并匹配 if letelse ifelse if let 表达式。这相比 match 表达式一次只能将一个值与模式比较提供了更多灵活性。并且 Rust 并不要求一系列 if letelse ifelse if let 分支的条件相互关联。

示例 19-1 中的代码展示了一系列针对不同条件的检查来决定背景颜色应该是什么。为了达到这个例子的目的,我们创建了硬编码值的变量,真实程序中这些值可能来源于用户输入。

文件名:src/main.rs

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-01/src/main.rs}}

示例 19-1: 结合 if letelse ifelse if let 以及 else

如果用户指定了中意的颜色,将使用其作为背景颜色。如果没有指定中意的颜色且今天是星期二,背景颜色将是绿色。如果用户指定了他们的年龄字符串并能够成功将其解析为数字的话,我们将根据这个数字使用紫色或者橙色。最后,如果没有一个条件符合,背景颜色将是蓝色。

这个条件结构允许我们支持复杂的需求。使用这里硬编码的值,例子会打印出 Using purple as the background color

注意 if let 也可以像 match 分支那样引入并遮蔽现有变量:if let Ok(age) = age 引入了一个新的 age 变量,包含 Ok 变体中的值,从而遮蔽了之前的 age 变量。这意味着 if age > 30 条件需要位于这个代码块内部:不能将两个条件组合为 if let Ok(age) = age && age > 30,因为我们想与 30 比较的新 age 只有在大括号开启的新作用域内才有效。

if let 表达式的缺点在于其穷尽性没有为编译器所检查,而 match 表达式则检查了。如果去掉最后的 else 块而遗漏处理一些情况,编译器也不会警告这类可能的逻辑错误。

while let 条件循环

一个与 if let 结构类似的是 while let 条件循环,它允许只要模式匹配就一直进行 while 循环。在示例 19-2 展示了一个 while let 循环等待跨线程发送的消息,不过在这个示例中它检查一个 Result 而非 Option

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-02/src/main.rs:here}}

示例 19-2: 使用 while let 循环只要 rx.recv() 返回 Ok 就打印出其值

这个例子会打印出 123recv 方法从信道的接收端取出第一条消息并返回一个 Ok(value)。当在第十六章遇到 recv 时,我们直接 unwrap 了错误,或者使用 for 循环将其视为迭代器处理。不过如示例 19-2 所示,我们也可以使用 while let,因为 recv 方法只要发送端持续产生消息它就一直返回 Ok,并在发送端断开连接后产生一个 Err

for 循环

for 循环中,模式是 for 关键字直接跟随的值。例如,在 for x in y 中,x 就是这个模式。示例 19-3 中展示了如何使用 for 循环来解构,或拆开一个元组作为 for 循环的一部分:

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-03/src/main.rs:here}}

示例 19-3: 在 for 循环中使用模式来解构元组

示例 19-3 的代码会打印出:

{{#include ../listings/ch19-patterns-and-matching/listing-19-03/output.txt}}

这里使用 enumerate 方法适配一个迭代器来产生一个值和其在迭代器中的索引,它们位于一个元组中。第一个产生的值是元组 (0, 'a')。当这个值匹配模式 (index, value)index 将会是 0value 将会是 'a',并打印出第一行输出。

let 语句

在本章之前,我们只明确讨论过通过 matchif let 使用模式,不过事实上也在别的地方使用过模式,包括 let 语句。例如,考虑一下这个直白的 let 变量赋值:

let x = 5;

不过你可能没有发觉,每一次像这样使用 let 语句就是在使用模式!let 语句更为正式的样子如下:

let PATTERN = EXPRESSION;

let x = 5; 这样的语句中变量名位于 PATTERN 位置,变量名不过是形式特别朴素的模式。我们将表达式与模式比较,并为任何找到的名称赋值。所以例如 let x = 5; 的情况,x 是一个代表 “将匹配到的值绑定到变量 x” 的模式。同时因为名称 x 是整个模式,这个模式实际上等于 “将任何值绑定到变量 x,不管值是什么”。

为了更清楚的理解 let 的模式匹配方面的内容,考虑示例 19-4 中使用 let 和模式解构一个元组:

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-04/src/main.rs:here}}

示例 19-4: 使用模式解构元组并一次创建三个变量

这里将一个元组与模式匹配。Rust 会比较值 (1, 2, 3) 与模式 (x, y, z),并发现二者具有相同的元素数量,因此匹配成功,于是将 1 绑定到 x,将 2 绑定到 y,将 3 绑定到 z。你可以将这个元组模式看作是将三个独立的变量模式结合在一起。

如果模式中元素的数量不匹配元组中元素的数量,则整个类型不匹配,并会得到一个编译时错误。例如,示例 19-5 展示了尝试用两个变量解构三个元素的元组,这是不行的:

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-05/src/main.rs:here}}

示例 19-5: 一个错误的模式结构,其中变量的数量不符合元组中元素的数量

尝试编译这段代码会给出如下类型错误:

{{#include ../listings/ch19-patterns-and-matching/listing-19-05/output.txt}}

为了修复这个错误,可以使用 _.. 来忽略元组中一个或多个值,如 [“忽略模式中的值”][ignoring-values-in-a-pattern] 部分所示。如果问题是模式中有太多的变量,则解决方法是通过去掉变量使得变量数与元组中元素数相等。

函数参数

函数参数也可以是模式。示例 19-6 中的代码声明了一个叫做 foo 的函数,它获取一个 i32 类型的参数 x,现在这看起来应该很熟悉:

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-06/src/main.rs:here}}

示例 19-6: 在参数中使用模式的函数签名

x 部分就是一个模式!类似于之前对 let 所做的,可以在函数参数中匹配元组。示例 19-7 将传递给函数的元组拆分为各个值:

文件名:src/main.rs

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-07/src/main.rs}}

示例 19-7: 一个在参数中解构元组的函数

这会打印出 Current location: (3, 5)。值 &(3, 5) 会匹配模式 &(x, y),如此 x 得到了值 3,而 y 得到了值 5

因为如第十三章所讲闭包类似于函数,也可以在闭包参数列表中使用模式。

现在我们见过了很多使用模式的方式了,不过模式在每个使用它的地方并不以相同的方式工作;在一些地方,模式必须是 irrefutable 的,意味着它们必须匹配所提供的任何值。在另一些情况,它们则可以是 refutable 的。接下来让我们讨论这两个概念。


Refutability(可反驳性): 模式是否会匹配失效

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

函数参数、let 语句和 for 循环只能接受不可反驳的模式,因为当值不匹配时,程序无法进行有意义的操作。if letwhile let 表达式可以接受可反驳和不可反驳的模式,但编译器会对不可反驳的模式发出警告,因为根据定义它们旨在处理可能的失败:条件表达式的功能在于它能够根据成功或失败来执行不同的操作。

通常我们无需担心可反驳和不可反驳模式的区别,不过确实需要熟悉可反驳性的概念,这样当在错误信息中看到时就知道如何应对。遇到这些情况,根据代码行为的意图,需要修改模式或者使用模式的结构。

让我们看看一个尝试在 Rust 要求不可反驳模式的地方使用可反驳模式以及相反情况的例子。在示例 19-8 中,有一个 let 语句,不过模式被指定为可反驳模式 Some(x)。如你所见,这不能编译:

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-08/src/main.rs:here}}

示例 19-8: 尝试在 let 中使用可反驳模式

如果 some_option_value 的值是 None,其不会成功匹配模式 Some(x),表明这个模式是可反驳的。然而,因为 let 对于 None 匹配不能产生任何合法的代码,所以 let 语句只能接受不可反驳模式。Rust 会在编译时抱怨我们尝试在要求不可反驳模式的地方使用可反驳模式:

{{#include ../listings/ch19-patterns-and-matching/listing-19-08/output.txt}}

因为我们没有覆盖(也不可能覆盖!)到模式 Some(x) 的每一个可能的值,所以 Rust 会合理地抗议。

为了修复在需要不可反驳模式的地方使用可反驳模式的情况,可以修改使用模式的代码:不同于使用 let,可以使用 if let。如此,如果模式不匹配,大括号中的代码将被忽略,其余代码保持有效。示例 19-9 展示了如何修复示例 19-8 中的代码。

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-09/src/main.rs:here}}

示例 19-9: 使用 if let 和一个带有可反驳模式的代码块来代替 let

我们给代码留了一条后路!现在这段代码已经完全有效了。然而,如果我们给 if let 提供一个不可反驳模式(即总会匹配的模式),例如示例 19-10 中的 x,编译器就会给出警告:

{{#rustdoc_include ../listings/ch19-patterns-and-matching/listing-19-10/src/main.rs:here}}

示例 19-10: 尝试把不可反驳模式用到 if let

Rust 会抱怨将不可反驳模式用于 if let 是没有意义的:

{{#include ../listings/ch19-patterns-and-matching/listing-19-10/output.txt}}

基于此,match 匹配分支必须使用可反驳模式,除了最后一个分支需要使用能匹配任何剩余值的不可反驳模式。Rust 允许我们在只有一个匹配分支的match 中使用不可反驳模式,不过这么做不是特别有用,并可以被更简单的 let 语句替代。

目前我们已经讨论了所有可以使用模式的地方,以及可反驳模式与不可反驳模式的区别,下面让我们一起去把可以用来创建模式的语法过目一遍吧。

http://www.dtcms.com/a/565203.html

相关文章:

  • Excel怎么快速合并当前工作簿下的所有工作表?
  • 网站建设捌金手指花总十六永久链接生成器
  • Nestjs框架: 微服务事件驱动通信与超时处理机制优化基于Event-Based 通信及异常捕获实践
  • html网站建设案例杭州发布官网
  • C语言实现观察者模式
  • JAVA算法练习题day62
  • SAP PP 生产报废单传输接口分享
  • 数据结构——三十八、查找的基本概念(王道408)
  • 深蓝学院 概率图模型
  • Kanass零基础学习,如何快速导入Jira、Mantis数据
  • 漳州网站建设多少钱创业计划书模板
  • linux vscode+cmake+clangd
  • 如何在 Linux 中获取更多信息
  • equals()与hashCode()之间的关系
  • Visual Studio Code 控制台乱码问题
  • 网站实现中英文asp.net网站很快吗
  • 公司网站链接建设电影网站论文
  • nvm切换node版本时,npm不跟着切换解决
  • iOS 应用逆向对抗手段,多工具组合实战(iOS 逆向防护/IPA 混淆/无源码加固/Ipa Guard CLI 实操)
  • x86架构的Ubuntu 22系统上,备份ISO镜像
  • 死锁防范:四大条件与破解之道
  • 考研408--数据结构--day1--基础概念时间、空间复杂度
  • 网站建设服务标准自己做热图的网站
  • WordPress如何设置站点名称做摄影网站的目的
  • Git创建合并分支、多人协作
  • 怎么做地下彩票网站郑州做网站那家做的好
  • 网站这么做项目ppt制作模板
  • 有什么做logo网站淮北矿业集团工程建设公司网站
  • 基于springboot的大型商场应急预案管理系统
  • 凌恩又升级内容啦!160+项分析!