Skip to content

Commit 7799977

Browse files
committed
added documentation for if let
1 parent 29c359e commit 7799977

File tree

1 file changed

+94
-3
lines changed

1 file changed

+94
-3
lines changed

src/expressions/match-expr.md

Lines changed: 94 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,25 @@ MatchExpression ->
99
MatchArms?
1010
`}`
1111
12-
Scrutinee -> Expression _except [StructExpression]_
12+
Scrutinee ->
13+
Expression _except_ [StructExpression]
1314
1415
MatchArms ->
1516
( MatchArm `=>` ( ExpressionWithoutBlock `,` | ExpressionWithBlock `,`? ) )*
1617
MatchArm `=>` Expression `,`?
1718
18-
MatchArm -> OuterAttribute* Pattern MatchArmGuard?
19+
MatchArm ->
20+
OuterAttribute* Pattern MatchArmGuard?
1921
20-
MatchArmGuard -> `if` Expression
22+
MatchArmGuard ->
23+
`if` MatchConditions
24+
25+
MatchConditions ->
26+
MatchCondition ( `&&` MatchCondition )*
27+
28+
MatchCondition ->
29+
OuterAttribute* `let` Pattern `=` Scrutinee
30+
| Expression
2131
```
2232
<!-- TODO: The exception above isn't accurate, see https://github.com/rust-lang/reference/issues/569 -->
2333

@@ -150,6 +160,87 @@ This allows shared borrows to be used inside guards without moving out of the sc
150160
r[expr.match.guard.no-mutation]
151161
Moreover, by holding a shared reference while evaluating the guard, mutation inside guards is also prevented.
152162
163+
r[expr.match.if.let.guard]
164+
## If Let Guards
165+
Match arms can include `if let` guards to allow conditional pattern matching within the guard clause.
166+
167+
r[expr.match.if.let.guard.syntax]
168+
```rust,ignore
169+
match expression {
170+
pattern if let subpattern = guard_expr => arm_body,
171+
...
172+
}
173+
```
174+
Here, `guard_expr` is evaluated and matched against `subpattern`. If the `if let` expression in the guard matches successfully, the arm's body is executed. Otherwise, pattern matching continues to the next arm.
175+
176+
r[expr.match.if.let.guard.behavior]
177+
When the pattern matches successfully, the `if let` expression in the guard is evaluated:
178+
* The guard proceeds if the inner pattern (`subpattern`) matches the result of `guard_expr`.
179+
* Otherwise, the next arm is tested.
180+
181+
```rust,ignore
182+
let value = Some(10);
183+
184+
let msg = match value {
185+
Some(x) if let Some(y) = Some(x - 1) => format!("Matched inner value: {}", y),
186+
_ => "No match".to_string(),
187+
};
188+
```
189+
190+
r[expr.match.if.let.guard.scope]
191+
* The `if let` guard may refer to variables bound by the outer match pattern.
192+
* New variables bound inside the `if let` guard (e.g., `y` in the example above) are available within the body of the match arm where the guard evaluates to `true`, but are not accessible in other arms or outside the match expression.
193+
194+
```rust,ignore
195+
let opt = Some(42);
196+
197+
match opt {
198+
Some(x) if let Some(y) = Some(x + 1) => {
199+
// Both `x` and `y` are available in this arm,
200+
// since the pattern matched and the guard evaluated to true.
201+
println!("x = {}, y = {}", x, y);
202+
}
203+
_ => {
204+
// `y` is not available here --- it was only bound inside the guard above.
205+
// Uncommenting the line below will cause a compile-time error:
206+
// println!("{}", y); // error: cannot find value `y` in this scope
207+
}
208+
}
209+
210+
// Outside the match expression, neither `x` nor `y` are in scope.
211+
```
212+
213+
* The outer pattern variables (`x`) follow the same borrowing behavior as in standard match guards (see below).
214+
215+
r[expr.match.if.let.guard.borrowing]
216+
Variables bound by the outer pattern follow the same borrowing rules as standard match guards:
217+
* A shared reference is taken to pattern variables before guard evaluation
218+
* Values are moved or copied only when the guard succeeds
219+
* Moving from outer pattern variables within the guard is restricted
220+
221+
```rust,ignore
222+
fn take<T>(value: T) -> Option<T> { Some(value) }
223+
224+
let val = Some(vec![1, 2, 3]);
225+
match val {
226+
Some(v) if let Some(_) = take(v) => "moved", // ERROR: cannot move `v`
227+
Some(v) if let Some(_) = take(v.clone()) => "cloned", // OK
228+
_ => "none",
229+
}
230+
```
231+
> [!NOTE]
232+
> Multiple matches using the `|` operator can cause the pattern guard and the side effects it has to execute multiple times. For example:
233+
> ```rust,ignore
234+
> use std::cell::Cell;
235+
>
236+
> let i: Cell<i32> = Cell::new(0);
237+
> match 1 {
238+
> 1 | _ if let Some(_) = { i.set(i.get() + 1); Some(1) } => {}
239+
> _ => {}
240+
> }
241+
> assert_eq!(i.get(), 2); // Guard is executed twice
242+
> ```
243+
153244
r[expr.match.attributes]
154245
## Attributes on match arms
155246

0 commit comments

Comments
 (0)