Skip to content

Commit 8e4a6c9

Browse files
committed
propose if let guard
1 parent c892139 commit 8e4a6c9

File tree

1 file changed

+74
-0
lines changed

1 file changed

+74
-0
lines changed

text/0000-if-let-guard.md

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
- Feature Name: if_let_guard
2+
- Start Date: 2018-01-15
3+
- RFC PR:
4+
- Rust Issue:
5+
6+
# Summary
7+
[summary]: #summary
8+
9+
Allow `if let` guards in `match` expressions.
10+
11+
# Motivation
12+
[motivation]: #motivation
13+
14+
This feature would greatly simplify some logic where we must match a pattern iff some value computed from the `match`-bound values has a certain form, where said value may be costly or impossible (due to affine semantics) to recompute in the match arm.
15+
16+
For further motivation, see the example in the guide-level explanation. Absent this feature, we might rather write the following:
17+
```rust
18+
match ui.wait_event() {
19+
KeyPress(mod_, key, datum) =>
20+
if let Some(action) = intercept(mod_, key) { act(action, datum) }
21+
else { accept!(KeyPress(mod_, key, datum)) /* can't re-use event verbatim if `datum` is non-`Copy` */ }
22+
ev => accept!(ev),
23+
}
24+
```
25+
26+
`accept` may in general be lengthy and inconvenient to move into another function, for example if it refers to many locals.
27+
28+
The code is clearer with an `if let` guard as follows.
29+
30+
# Guide-level explanation
31+
[guide-level-explanation]: #guide-level-explanation
32+
33+
*(Adapted from Rust book)*
34+
35+
A *match guard* is an `if let` condition specified after the pattern in a `match` arm that also must match if the pattern matches in order for that arm to be chosen. Match guards are useful for expressing more complex ideas than a pattern alone allows.
36+
37+
The condition can use variables created in the pattern, and the match arm can use any variables bound in the `if let` pattern (as well as any bound in the `match` pattern, unless the `if let` expression moves out of them).
38+
39+
Let us consider an example which accepts a user-interface event (e.g. key press, pointer motion) and follows 1 of 2 paths: either we intercept it and take some action or deal with it normally (whatever that might mean here):
40+
```rust
41+
match ui.wait_event() {
42+
KeyPress(mod_, key, datum) if let Some(action) = intercept(mod_, key) => act(action, datum),
43+
ev => accept!(ev),
44+
}
45+
```
46+
47+
# Reference-level explanation
48+
[reference-level-explanation]: #reference-level-explanation
49+
50+
This proposal would introduce syntax for a match arm: `pat if let guard_pat = guard_expr => body_expr` with semantics so the arm is chosen iff the argument of `match` matches `pat` and `guard_expr` matches `guard_pat`. The variables of `pat` are bound in `guard_expr`, and the variables of `pat` and `guard_pat` are bound in `body_expr`. The syntax is otherwise the same as for `if` guards. (Indeed, `if` guards become effectively syntactic sugar for `if let` guards.)
51+
52+
An arm may not have both an `if` and an `if let` guard.
53+
54+
# Drawbacks
55+
[drawbacks]: #drawbacks
56+
57+
* It further complicates the grammar.
58+
* It is ultimately syntactic sugar, but the transformation to present Rust is potentially non-obvious.
59+
60+
# Rationale and alternatives
61+
[alternatives]: #alternatives
62+
63+
* The chief alternatives are to rewrite the guard as an `if` guard and a bind in the match arm, or in some cases into the argument of `match`; or to write the `if let` in the match arm and copy the rest of the `match` into the `else` branch — what can be done with this syntax can already be done in Rust (to the author's knowledge); this proposal is purely ergonomic, but in the author's opinion, the ergonomic win is significant.
64+
* The proposed syntax feels natural by analogy to the `if` guard syntax we already have, as between `if` and `if let` expressions. No alternative syntaxes were considered.
65+
66+
# Unresolved questions
67+
[unresolved]: #unresolved-questions
68+
69+
Questions in scope of this proposal: none yet known
70+
71+
Questions out of scope:
72+
73+
* Should we allow multiple guards? This proposal allows only a single `if let` guard. One can combine `if` guards with `&&`[an RFC](https://github.com/rust-lang/rfcs/issues/929) to allow `&&` in `if let` already is, so we may want to follow that in future for `if let` guards also.
74+
* What happens if `guard_expr` moves out of `pat` but fails to match? This is already a question for `if` guards and (to the author's knowledge) not formally specified anywhere — this proposal (implicitly) copies that behavior.

0 commit comments

Comments
 (0)