16-match 表达式
4 分钟阅读
译文 · 基于 The Rust Reference
原文链接: https://doc.rust-lang.org/reference/expressions/match-expr.html
r[expr.match]
match 表达式
r[expr.match.syntax]
MatchExpression ->
`match` Scrutinee `{`
InnerAttribute*
MatchArms?
`}`
Scrutinee -> Expression _except [StructExpression]_
MatchArms ->
( MatchArm `=>` ( ExpressionWithoutBlock `,` | ExpressionWithBlock `,`? ) )*
MatchArm `=>` Expression `,`?
MatchArm -> OuterAttribute* Pattern MatchArmGuard?
MatchArmGuard -> `if` MatchConditions
MatchConditions ->
MatchGuardChain
| Expression
MatchGuardChain -> MatchGuardCondition ( `&&` MatchGuardCondition )*
MatchGuardCondition ->
Expression _except [ExcludedMatchConditions]_
| OuterAttribute* `let` Pattern `=` MatchGuardScrutinee
MatchGuardScrutinee -> Expression _except [ExcludedMatchConditions]_
@root ExcludedMatchConditions ->
LazyBooleanExpression
| RangeExpr
| RangeFromExpr
| RangeInclusiveExpr
| AssignmentExpression
| CompoundAssignmentExpression
r[expr.match.intro]
match 表达式根据模式进行分支。具体发生何种匹配取决于模式。
r[expr.match.scrutinee]
match 表达式有一个*被检视表达式*,即要与各模式比较的值。
r[expr.match.scrutinee-constraint] 被检视表达式与各模式必须具有相同的类型。
r[expr.match.scrutinee-behavior]
match 的行为取决于被检视表达式是位置表达式还是值表达式。
r[expr.match.scrutinee-value]
若被检视表达式是值表达式,则首先将其求值到一个临时位置,然后将得到的值依次与各分支中的模式比较,直到找到匹配。第一个模式匹配成功的分支被选为 match 的跳转目标,模式所绑定的变量被赋给该分支块中的局部变量,然后控制进入该块。
r[expr.match.scrutinee-place] 当被检视表达式是位置表达式时,match 不会分配临时位置;不过,按值绑定仍可能从该内存位置复制或移出。在可能的情况下,更推荐对位置表达式进行匹配,因为这些匹配的生命周期继承自该位置表达式,而不是被限制在 match 内部。
match 表达式的示例:
| |
r[expr.match.pattern-vars] 模式中绑定的变量作用域为 match 守卫以及该分支的表达式。
r[expr.match.pattern-var-binding] 绑定模式(移动、复制或引用)取决于该模式。
r[expr.match.or-pattern]
多个匹配模式可以用 | 运算符连接。各模式会按从左到右的顺序测试,直到找到成功的匹配。
| |
r[expr.match.or-patterns-restriction]
由 | 分隔的每个模式中的每个绑定,都必须出现在该分支的所有模式中。
r[expr.match.binding-restriction] 同名的每个绑定必须具有相同的类型,并且具有相同的绑定模式。
r[expr.match.type]
整个 match 表达式的类型是各个 match 分支的最小上界。
r[expr.match.empty]
若没有任何 match 分支,则该 match 表达式是发散的,类型为 !。
| |
r[expr.match.diverging]
若被检视表达式发散,或所有 match 分支都发散,则整个 match 表达式也发散。
r[expr.match.guard]
match 守卫
r[expr.match.guard.intro] match 分支可以接受 match 守卫,以进一步细化匹配某一情况的条件。
r[expr.match.guard.condition]
模式守卫出现在模式之后、跟在 if 关键字后面,由具有[布尔类型][type.bool]的[表达式][Expression]或条件式 let 匹配组成。
r[expr.match.guard.behavior]
当模式匹配成功时,会执行模式守卫。若所有守卫条件操作数都求值为 true,且所有 let 模式都成功匹配其被检视表达式,则该 match 分支匹配成功,并执行分支体。
r[expr.match.guard.next]
否则,会测试下一个模式,包括同一分支中用 | 运算符连接的其它匹配。
| |
注意 使用
|运算符的多个匹配可能导致模式守卫及其副作用执行多次。例如:
1 2 3 4 5 6 7# use std::cell::Cell; let i : Cell<i32> = Cell::new(0); match 1 { 1 | _ if { i.set(i.get() + 1); false } => {} _ => {} } assert_eq!(i.get(), 2);
r[expr.match.guard.bound-variables] 模式守卫可以引用紧随其后的模式中所绑定的变量。
r[expr.match.guard.shared-ref] 在求值守卫之前,会对变量所匹配的那部分被检视表达式取共享引用。求值守卫时,访问该变量就使用这个共享引用。
r[expr.match.guard.value] 只有当守卫求值成功时,才会从被检视表达式把值移动或复制到该变量中。这允许在守卫中使用共享借用,而不会在守卫匹配失败时从被检视表达式中移出。
r[expr.match.guard.no-mutation] 此外,通过在求值守卫时持有共享引用,也阻止了在守卫内部进行修改。
r[expr.match.guard.let]
守卫可以使用 let 模式来有条件地匹配被检视表达式,并在模式匹配成功时把新变量绑定到作用域中。
本例中会求值守卫条件 let Some(first_char) = name.chars().next()。若该 let 模式匹配成功(即字符串至少有一个字符),则执行该分支的函数体。否则,模式匹配继续到下一个分支。
该 let 模式会创建新绑定(first_char),它可以与原始模式绑定(name)一起在该分支的函数体中使用。
| |
r[expr.match.guard.chains]
match 守卫链
r[expr.match.guard.chains.intro]
多个守卫条件操作数可以用 && 分隔。
| |
r[expr.match.guard.chains.order]
与 && [LazyBooleanExpression] 类似,各操作数从左到右求值,直到某个操作数求值为 false 或某个 let 匹配失败,此后的操作数不再求值。
r[expr.match.guard.chains.bindings]
每个 let 模式的绑定都会进入作用域,供下一个条件操作数和 match 分支体使用。
r[expr.match.guard.chains.or]
若任一守卫条件操作数是 let 模式,则任何条件操作数都不能是 || [惰性布尔运算符表达式][expr.bool-logic],以免与 let 被检视表达式产生歧义和优先级问题。
若需要 || 表达式,可以使用圆括号。例如:
| |
r[expr.match.attributes]
match 分支上的属性
r[expr.match.attributes.outer]
match 分支上允许外部属性。在 match 分支上有意义的属性只有 cfg 和 lint 检查属性。
r[expr.match.attributes.inner] 内部属性允许直接出现在 match 表达式的开括号之后,其表达式上下文与块表达式上的属性相同。