Skip to content

Commit 2a39499

Browse files
ehussdianne
authored andcommitted
Rework match guard chain
This applies some editorial rework, mainly to match the style of `if` conditions.
1 parent 8ce1dc6 commit 2a39499

File tree

2 files changed

+63
-63
lines changed

2 files changed

+63
-63
lines changed

src/expressions/match-expr.md

Lines changed: 60 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,23 @@ MatchExpression ->
99
MatchArms?
1010
`}`
1111
12-
Scrutinee ->
13-
Expression _except_ [StructExpression]
12+
Scrutinee -> Expression _except_ [StructExpression]
1413
1514
MatchArms ->
1615
( MatchArm `=>` ( ExpressionWithoutBlock `,` | ExpressionWithBlock `,`? ) )*
1716
MatchArm `=>` Expression `,`?
1817
19-
MatchArm ->
20-
OuterAttribute* Pattern MatchArmGuard?
18+
MatchArm -> OuterAttribute* Pattern MatchArmGuard?
2119
22-
MatchArmGuard ->
23-
`if` MatchConditions
20+
MatchArmGuard -> `if` MatchConditions
2421
2522
MatchConditions ->
2623
Expression
27-
| MatchConditionChain
24+
| MatchGuardChain
2825
29-
MatchConditionChain ->
30-
MatchChainCondition ( `&&` MatchChainCondition )*
26+
MatchGuardChain -> MatchGuardCondition ( `&&` MatchGuardCondition )*
3127
32-
MatchChainCondition ->
28+
MatchGuardCondition ->
3329
Expression _except [ExcludedMatchConditions]_
3430
| OuterAttribute* `let` Pattern `=` MatchGuardScrutinee
3531
@@ -126,12 +122,11 @@ r[expr.match.guard]
126122
r[expr.match.guard.intro]
127123
Match arms can accept _match guards_ to further refine the criteria for matching a case.
128124

129-
r[expr.match.guard.type]
130-
Pattern guards appear after the pattern and consist of a `bool`-typed expression following the `if` keyword.
125+
r[expr.match.guard.condition]
126+
Pattern guards appear after the pattern following the `if` keyword and consist of an [Expression] with a [boolean type][type.bool] or a conditional `let` match.
131127

132128
r[expr.match.guard.behavior]
133-
When the pattern matches successfully, the pattern guard expression is executed.
134-
If the expression evaluates to true, the pattern is successfully matched against.
129+
When the pattern matches successfully, the pattern guard is executed. If all of the guard condition operands evaluate to `true` and all of the `let` patterns successfully match their [scrutinee]s, the match arm is successfully matched against and the arm body is executed.
135130

136131
r[expr.match.guard.next]
137132
Otherwise, the next pattern, including other matches with the `|` operator in the same arm, is tested.
@@ -168,68 +163,73 @@ Before evaluating the guard, a shared reference is taken to the part of the scru
168163
While evaluating the guard, this shared reference is then used when accessing the variable.
169164
170165
r[expr.match.guard.value]
171-
Only when the guard evaluates to true is the value moved, or copied, from the scrutinee into the variable.
166+
Only when the guard evaluates successfully is the value moved, or copied, from the scrutinee into the variable.
172167
This allows shared borrows to be used inside guards without moving out of the scrutinee in case guard fails to match.
173168
174169
r[expr.match.guard.no-mutation]
175170
Moreover, by holding a shared reference while evaluating the guard, mutation inside guards is also prevented.
176171
177-
r[expr.match.if.let.guard]
178-
## If-let Guards
179-
Match arms can have additional conditions using *if-let guards*. These allow using `if let` expressions within match guard clauses, enabling conditional pattern matching directly in the guard.
172+
r[expr.match.guard.let]
173+
Guards can use `let` patterns to conditionally match a scrutinee and to bind new variables into scope when the pattern matches successfully.
180174
181-
r[expr.match.if.let.guard.syntax]
182-
```rust
183-
enum Command {
184-
Run(String),
185-
Stop,
186-
}
175+
> [!EXAMPLE]
176+
> In this example, the guard condition `let Some(first_char) = name.chars().next()` is evaluated. If the `if let` expression successfully matches (i.e., the string has at least one character), the arm's body is executed with both `name` and `first_char` available. Otherwise, pattern matching continues to the next arm.
177+
>
178+
> The key point is that the `if let` guard creates a new binding (`first_char`) that's only available if the guard succeeds, and this binding can be used alongside the original pattern bindings (`name`) in the arm's body.
179+
> ```rust
180+
> # enum Command {
181+
> # Run(String),
182+
> # Stop,
183+
> # }
184+
> let cmd = Command::Run("example".to_string());
185+
>
186+
> match cmd {
187+
> Command::Run(name) if let Some(first_char) = name.chars().next() => {
188+
> // Both `name` and `first_char` are available here
189+
> println!("Running: {name} (starts with '{first_char}')");
190+
> }
191+
> Command::Run(name) => {
192+
> println!("{name} is empty");
193+
> }
194+
> _ => {}
195+
> }
196+
> ```
187197
188-
match cmd {
189-
Command::Run(name) if let Some(first_char) = name.chars().next() => {
190-
// Both `name` and `first_char` are available here
191-
println!("Running: {} (starts with '{}')", name, first_char);
192-
}
193-
Command::Run(name) => {
194-
println!("Cannot run command: {}", name);
195-
}
196-
_ => {}
197-
}
198-
```
199-
Here, the guard condition `let Some(first_char) = name.chars().next()` is evaluated. If the `if let` expression successfully matches (i.e., the string has at least one character), the arm's body is executed with both `name` and `first_char` available. Otherwise, pattern matching continues to the next arm.
198+
r[expr.match.guard.chains]
199+
## Match guard chains
200200
201-
The key point is that the `if let` guard creates a new binding (`first_char`) that's only available if the guard succeeds, and this binding can be used alongside the original pattern bindings (`name`) in the arm's body.
201+
r[expr.match.guard.chains.intro]
202+
Multiple guard condition operands can be separated with `&&`.
202203
203-
r[expr.match.if.let.guard.behavior]
204-
When the pattern matches successfully, the `if let` expression in the guard is evaluated:
205-
* The guard proceeds if the inner pattern (`subpattern`) matches the result of `guard_expr`.
206-
* Otherwise, the next arm is tested.
204+
> [!EXAMPLE]
205+
> ```rust
206+
> # let foo = Some([123]);
207+
> # let already_checked = false;
208+
> match foo {
209+
> Some(xs) if let [single] = xs && !already_checked => { dbg!(single); }
210+
> _ => {}
211+
> }
212+
> ```
207213
208-
r[expr.match.if.let.guard.scope]
214+
r[expr.match.guard.chains.order]
215+
Similar to a `&&` [LazyBooleanExpression], each operand is evaluated from left-to-right until an operand evaluates as `false` or a `let` match fails, in which case the subsequent operands are not evaluated.
209216
210-
For detailed information about variable scope and drop behavior, see the [scope and drop section].
217+
r[expr.match.guard.chains.bindings]
218+
The bindings of each `let` pattern are put into scope to be available for the next condition operand and the match arm body.
211219
212-
```rust,ignore
213-
# fn take<T>(value: T) -> Option<T> { Some(value) }
220+
r[expr.match.guard.chains.or]
221+
If any guard condition operand is a `let` pattern, then none of the condition operands can be a `||` [lazy boolean operator expression][expr.bool-logic] due to ambiguity and precedence with the `let` scrutinee.
214222
215-
let val = Some(vec![1, 2, 3]);
216-
match val {
217-
Some(v) if let Some(_) = take(v) => "moved", // ERROR: cannot move `v`
218-
Some(v) if let Some(_) = take(v.clone()) => "cloned", // OK
219-
_ => "none",
220-
}
221-
```
222-
> [!NOTE]
223-
> Multiple matches using the `|` operator can cause the pattern guard and the side effects it has to execute multiple times. For example:
224-
> ```rust
225-
> use std::cell::Cell;
223+
> [!EXAMPLE]
224+
> If a `||` expression is needed, then parentheses can be used. For example:
226225
>
227-
> let i: Cell<i32> = Cell::new(0);
228-
> match 1 {
229-
> 1 | _ if let Some(_) = { i.set(i.get() + 1); Some(1) } => {}
226+
> ```rust
227+
> # let foo = Some(123);
228+
> match foo {
229+
> // Parentheses are required here.
230+
> Some(x) if (x < -100 || x > 20) => {}
230231
> _ => {}
231232
> }
232-
> assert_eq!(i.get(), 2); // Guard is executed twice
233233
> ```
234234
235235
r[expr.match.attributes]

src/names/scopes.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ r[names.scopes.pattern-bindings.let-chains]
5454
* [`if let`] and [`while let`] bindings are valid in the following conditions as well as the consequent block.
5555
r[names.scopes.pattern-bindings.match-arm]
5656
* [`match` arms] bindings are within the [match guard] and the match arm expression.
57-
r[names.scopes.pattern-bindings.match-guard-if-let]
58-
* [`if let` guard] bindings are within the guard expression and the match arm expression if the guard succeeds.
57+
r[names.scopes.pattern-bindings.match-guard-let]
58+
* [`match` guard `let`] bindings are valid in the following guard conditions and the match arm expression if the guard succeeds.
5959

6060
r[names.scopes.pattern-bindings.items]
6161
Local variable scopes do not extend into item declarations.
@@ -349,7 +349,7 @@ impl ImplExample {
349349
[`macro_use` prelude]: preludes.md#macro_use-prelude
350350
[`macro_use`]: ../macros-by-example.md#the-macro_use-attribute
351351
[`match` arms]: ../expressions/match-expr.md
352-
[`if let` guard]: ../expressions/match-expr.md#if-let-guards
352+
[`match` guard `let`]: expr.match.guard.let
353353
[`Self`]: ../paths.md#self-1
354354
[Associated consts]: ../items/associated-items.md#associated-constants
355355
[associated items]: ../items/associated-items.md

0 commit comments

Comments
 (0)