Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

模式和模式匹配

模式是 X 语言中一个特殊的语法,用于匹配类型的结构,无论是简单的还是复杂的。与 when/is 表达式和其他构造一起使用模式使我们能够更好地控制程序的控制流。模式由以下部分的某种组合组成:

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

这些部分描述了我们要处理的数据的形状,然后我们将其与模式匹配,以确定我们的程序是否具有继续执行特定代码路径所需的正确形状数据。

在本章中,我们将查看所有可以使用模式的地方,所有有效模式类型,以及如何使用它们。

所有可以使用模式的地方

到目前为止,我们已经在几个上下文中非正式地使用了模式,但我们还没有讨论所有可以使用它们的地方。让我们开始一个全面的列表,列出所有模式有效的位置!

when/is 表达式

正如我们在第 6 章中讨论的,我们可以在 when/is 表达式中使用模式。作为提醒,when/is 表达式的一般形式是:

when VALUE is {
    PATTERN => EXPRESSION,
    PATTERN => EXPRESSION,
    _ => EXPRESSION,
}

when/is 表达式必须是穷尽的,这意味着 VALUE 的每个可能的可能性都必须被覆盖。特定的模式 _ 是一个很好的包罗万象的模式,它匹配任何内容而不绑定到变量,因此它对于最后一个 arm 很有用。

if let 条件表达式

正如我们在第 6 章中讨论的,if let 表达式主要是作为编写等效于仅匹配一个情况的 when/is 表达式的简写而构造的。可选地,if let 可以有一个相应的 else,其中包含如果 if let 中的模式不匹配则运行的代码。

示例显示了可以混合和匹配 if letelse ifelse if let 表达式。这比 when/is 表达式更灵活,因为 when/is 只能有一个值和一组针对其第一个 arm 进行检查的模式。此外,X 语言不要求 if letelse ifelse if let arm 中的条件相互关联。

让我们看一下:

let favorite_color: Option<String> = None
let is_tuesday = false
let age: Result<integer, String> = Ok(34)

if let Some(color) = favorite_color {
  println("使用你喜欢的颜色 {}, 作为背景", color)
} else if is_tuesday {
  println("星期二是绿色的日子!")
} else if let Ok(age) = age {
  if age > 30 {
    println("使用橙色作为背景色")
  } else {
    println("使用紫色作为背景色")
  }
} else {
  println("使用蓝色作为背景色")
}

这段代码可以运行,但不一定有意义(我们选择的颜色决策不一定基于任何真实标准)。重要的是要看看 if let 的灵活性允许我们表达什么。

while let 条件循环

while let 条件循环与 if let 类似,只要模式继续匹配,它就会允许 while 循环运行。示例显示了一个使用 while let 循环的示例,只要 pop() 调用返回 Some,该循环就会打印向量中的每个值。当它返回 None 时,循环停止。

let mut stack = List::new()
stack = stack + [1]
stack = stack + [2]
stack = stack + [3]

while let Some(top) = stack.pop() {
  println("{}", top)
}

这段代码将打印 3、2,然后是 1。pop 方法获取列表的最后一个元素并返回 Some(value)。如果列表为空,pop 返回 Nonewhile let 循环继续运行循环体,只要 pop 返回 Some。当它返回 None 时,循环停止。我们可以使用 while let 来弹出栈中的每个元素。

for 循环

正如我们在第 3 章中讨论的,在 for 循环中,直接在 for 关键字后面的是模式。例如,在 for x in y 中,x 是模式。示例显示了如何在 for 循环中使用模式来解构元组,因为 for 循环是 for 循环的一部分:

let v = [(1, 'a'), (2, 'b'), (3, 'c')]

for (index, value) in v.iter().enumerate() {
  println("{} 在索引 {}", value, index)
}

因为我们使用 enumerate 方法适配了迭代器,所以它每次迭代都会产生一个元组,其中包含索引和该索引处的值。第一次迭代将产生元组 (0, (1, 'a'))。当我们将此值与模式 (index, value) 匹配时,index 将为 0value 将为 (1, 'a')

这段代码的输出如下:

(1, 'a') 在索引 0
(2, 'b') 在索引 1
(3, 'c') 在索引 2

let 语句

在本章之前,我们只讨论了使用模式与 when/isif let,但我们也可以在 let 语句中使用模式!例如,考虑这个带有 let 的简单变量赋值:

let x = 5

x 在这里是一个模式!每次我们使用 let 语句时,我们都在使用模式,即使我们没有意识到这一点!让我们看一个更复杂的 let 示例:使用模式来解构元组。

let (x, y, z) = (1, 2, 3)

这里我们用一个元组匹配一个模式。如果模式中的元素数量与元组中的元素数量匹配,X 语言会将元组中的每个值绑定到模式中的相应变量。在这种情况下,1 将绑定到 x2 将绑定到 y3 将绑定到 z

如果模式中的元素数量与元组中的元素数量不匹配,整个类型将不匹配,我们将得到编译错误。

函数参数

let 类似,函数参数也可以是模式!让我们看一个例子:

function print_coordinates(&(x, y): &(integer, integer)) {
  println("当前位置: ({}, {})", x, y)
}

function main() {
  let point = (3, 5)
  print_coordinates(&point)
}

这段代码将打印 当前位置: (3, 5)。值 &(3, 5) 匹配模式 &(x, y),所以 x 是值 3y 是值 5

我们也可以在闭包参数列表中使用模式,就像我们在函数参数列表中使用它们一样,因为闭包类似于函数,正如我们在第 13 章中讨论的那样。

好的,所以我们已经看到了很多使用模式的地方,但模式在我们使用它们的所有地方的行为并不相同;在某些地方,模式必须是不可反驳的;在其他地方,它们可能是可反驳的。让我们接下来讨论这个区别。