You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This RFC proposes to add a new kind of pattern, the **guard pattern.** Like match arm guards, guard patterns restrict another pattern to match only if an expression evaluates to `true`. The syntax for guard patterns, `pat if condition`, is compatible with match arm guard syntax, so existing guards can be superceded by guard patterns without breakage.
10
11
11
12
# Motivation
13
+
12
14
[motivation]: #motivation
13
15
14
16
Guard patterns, unlike match arm guards, can be nested within other patterns. In particular, guard patterns nested within or-patterns can depend on the branch of the or-pattern being matched. This has the potential to simplify certain match expressions, and also enables the use of guards in other places where refutable patterns are acceptable. Furthermore, by moving the guard condition closer to the bindings upon which it depends, pattern behavior can be made more local.
Guard patterns allow you to write guard expressions to decide whether or not something should match anywhere you can use a pattern, not just at the top level of `match` arms.
@@ -60,6 +63,7 @@ This is a **guard pattern**. It matches a value if `pattern` (the pattern it wra
60
63
For new users, guard patterns are better explained without reference to match arm guards. Instead, they can be explained by similar examples to the ones currently used for match arm guards, followed by an example showing that they can be nested within other patterns and used outside of match arms.
With `if let` and `while let` expressions now using `PatternNoTopGuard`. `let` statements and function parameters can continue to use `PatternNoTopAlt`.
129
133
130
134
## Bindings Available to Guards
131
135
132
136
The only bindings available to guard conditions are
137
+
133
138
- bindings from the scope containing the pattern match, if any; and
134
139
- bindings introduced by identifier patterns _within_ the guard pattern.
135
140
@@ -157,20 +162,6 @@ let (Struct { x, y } if x == y) = Struct { x: 0, y: 0 } else { /* ... */ }
157
162
158
163
In general, guards can, without changing meaning, "move outwards" until they reach an or-pattern where the condition can be different in other branches, and "move inwards" until they reach a level where the identifiers they reference are not bound.
159
164
160
-
## Bindings Must Still Match Across Disjunctions
161
-
162
-
This RFC does _not_ propose to change what bindings are allowed in disjunctions, even when those bindings are used only within guard patterns.
163
-
164
-
For example, the following code will error just like it would without any guard patterns:
165
-
166
-
```rust
167
-
matchSome(0) {
168
-
Some(xifx>0) |None=> {},
169
-
//~^ ERROR variable `x` is not bound in all patterns
170
-
_=> {},
171
-
}
172
-
```
173
-
174
165
## As Macro Arguments
175
166
176
167
Currently, `if` is in the follow set of `pat` and `pat_param` fragments, so top-level guards cannot be used as arguments for the current edition. This is identical to the situation with top-level or-patterns as macro arguments, and guard patterns will take the same approach:
@@ -180,11 +171,13 @@ Currently, `if` is in the follow set of `pat` and `pat_param` fragments, so top-
180
171
3. In the next edition, update `pat` fragments to accept `Pattern` once again.
181
172
182
173
# Drawbacks
174
+
183
175
[drawbacks]: #drawbacks
184
176
185
177
Rather than matching only by structural properties of ADTs, equality, and ranges of certain primitives, guards give patterns the power to express arbitrary restrictions on types. This necessarily makes patterns more complex both in implementation and in concept.
@@ -203,7 +196,7 @@ Therefore, we could choose to restrict guard patterns so that they appear only i
203
196
This RFC refers to this as "or-of-guards" patterns, because it changes or-patterns from or-ing together a list of patterns to or-ing together a list of optionally guarded patterns.
204
197
205
198
Note that, currently, most patterns are actually parsed as an or-pattern with only one choice.
206
-
Therefore, to achieve the effect of forcing patterns as far out as possible guards would only be allowed in or-patterns with more than one choice.
199
+
Therefore, to achieve the effect of forcing patterns as far out as possible guards would only be allowed in or-patterns with more than one choice.
207
200
208
201
There are, however, a couple reasons where it could be desirable to allow guards further inwards than strictly necessary.
209
202
@@ -245,6 +238,7 @@ match order {
245
238
### Pattern Macros
246
239
247
240
If guards can only appear immediately within or-patterns, then either
241
+
248
242
- pattern macros can emit guards at the top-level, in which case they can only be called immediately within or-patterns without risking breakage if the macro definition changes (even to another valid pattern!); or
249
243
- pattern macros cannot emit guards at the top-level, forcing macro authors to use terrible workarounds like `(Some(x) if guard(x)) | (Some(x) if false)` if they want to use the feature.
250
244
@@ -254,16 +248,15 @@ This can also be seen as a special case of the previous argument, as pattern mac
254
248
255
249
It may seem odd that we explicitly require const patterns to use pure `PartialEq` implementations (and the upcoming [proposal](https://hackmd.io/4qDDMcvyQ-GDB089IPcHGg) for deref patterns to use pure `Deref` implementations), but allow arbitrary side effects in guards. The ultimate reason for this is that, unlike const patterns and the proposed deref patterns, guard patterns are always refutable.
256
250
257
-
258
251
Without the requirement of `StructuralPartialEq` we could write a `PartialEq` implementation which always returns `false`, resulting either in UB or a failure to ensure match exhaustiveness:
259
252
260
253
```rust
261
254
constFALSE:EvilBool=EvilBool(false);
262
-
constTRUE:EvilBool=EvilBool(true);
255
+
constTRUE:EvilBool=EvilBool(true);
263
256
264
257
matchEvilBool(false) {
265
258
FALSE=> {},
266
-
TRUE=> {},
259
+
TRUE=> {},
267
260
}
268
261
```
269
262
@@ -289,45 +282,20 @@ match EvilBool(false) {
289
282
But this will always be a compilation error because the `match` statement is no longer assumed to be exhaustive.
290
283
291
284
# Prior art
285
+
292
286
[prior-art]: #prior-art
293
287
294
-
This feature has been implemented in the [Unison](https://www.unison-lang.org/docs/language-reference/guard-patterns/), [Wolfram](https://reference.wolfram.com/language/ref/Condition.html), and [E ](https://en.wikipedia.org/wiki/E_(programming_language)) languages.
288
+
This feature has been implemented in the [Unison](https://www.unison-lang.org/docs/language-reference/guard-patterns/), [Wolfram](https://reference.wolfram.com/language/ref/Condition.html), and [E ](<https://en.wikipedia.org/wiki/E_(programming_language)>) languages.
295
289
296
290
Guard patterns are also very similar to Haskell's [view patterns](https://ghc.gitlab.haskell.org/ghc/doc/users_guide/exts/view_patterns.html), which are more powerful and closer to a hypothetical "`if let` pattern" than a guard pattern as this RFC proposes it.
297
291
298
292
# Unresolved questions
299
-
[unresolved-questions]: #unresolved-questions
300
-
301
-
- How should we refer to this feature?
302
-
- "Guard pattern" will likely be most intuitive to users already familiar with match arm guards. Most likely, this includes anyone reading this, which is why this RFC uses that term.
303
-
- "`if`-pattern" agrees with the naming of or-patterns, and obviously matches the syntax well. This is probably the most intuitive name for new users learning the feature.
304
-
- Some other possibilities: "condition/conditioned pattern," "refinement/refined pattern," "restriction/restricted pattern," or "predicate/predicated pattern."
305
-
- What anti-patterns should we lint against?
306
-
- Using guard patterns to test equality or range membership when a literal or range pattern could be used instead?
307
-
- Using guard patterns at the top-level of `if let` or `while let` instead of let chains?
308
-
- Guard patterns within guard patterns instead of using one guard with `&&` in the condition?
309
-
-`foo @ (x if guard(x))` rather than `(foo @ x) if guard(x)`? Or maybe this is valid in some cases for localizing match behavior?
310
-
- Is `pat_no_top_guard` a good name, or should we use something shorter like `pat_unguarded`?
311
-
312
-
# Future possibilities
313
-
[future-possibilities]: #future-possibilities
314
-
315
-
## Allowing `if let`
316
-
317
-
Users expect to be able to write `if let` where they can write `if`. Allowing this in guard patterns would make them significantly more powerful, but also more complex.
318
-
319
-
One way to think about this is that patterns serve two functions:
320
293
321
-
1. Refinement: refutable patterns only match some subset of a type's values.
322
-
2. Destructuring: patterns use the structure common to values of that subset to extract data.
323
-
324
-
Guard patterns as described here provide _arbitrary refinement_. That is, guard patterns can match based on whether any arbitrary expression evaluates to true.
325
-
326
-
Allowing `if let` allows not just arbitrary refinement, but also _arbitrary destructuring_. The value(s) bound by an `if let` pattern can depend on the value of an arbitrary expression.
294
+
[unresolved-questions]: #unresolved-questions
327
295
328
296
## Allowing Mismatching Bindings When Possible
329
297
330
-
Users will likely want to write something like
298
+
Ideally, users would be able to write something to the effect of
331
299
332
300
```rust
333
301
matchSome(0) {
@@ -337,19 +305,42 @@ match Some(0) {
337
305
```
338
306
339
307
This is also very useful for macros, because it allows
308
+
340
309
1. pattern macros to use guard patterns freely without introducing new bindings the user has to be aware of in order to use the pattern macro within a disjunction, and
341
310
2. macro users to pass guard patterns to macros freely, even if the macro uses the pattern within a disjunction.
342
311
343
312
As mentioned above, this case is not covered by this RFC, because `x` would need to be bound in both cases of the disjunction.
344
313
345
-
However, we could support this by automatically detecting that `x` is not ever used outside of the guard pattern, and allowing the guard to capture the binding, so it wouldn't have to be bound in other cases of the disjunction.
314
+
### Possible Design
346
315
347
-
We could also make this capturing behavior explicit, with some kind of syntax extending guard patterns:
316
+
[@tmandry proposed](https://github.com/rust-lang/rfcs/pull/3637#issuecomment-2307839511) amending the rules for how names can be bound in patterns to the following:
348
317
349
-
```rust
350
-
// example syntax by analogy with closures
351
-
// probably not what we'd want to go with, since you can't specify which bindings are captured
352
-
Some(xmoveifx>0) |None
353
-
```
318
+
1. Unchanged: If a name is bound in any part of a pattern, it shadows existing definitions of the name.
319
+
2. Unchanged: If a name bound by a pattern is used in the body, it must be defined in every part of a disjunction and be the same type in each.
320
+
3. Removed: ~~Bindings introduced in one branch of a disjunction must be introduced in all branches.~~
321
+
4. Added: If a name is bound in multiple parts of a disjunction, it must be bound to the same type in every part. (Enforced today by the combination of 2 and 3.)
322
+
323
+
## How to Refer to Guard Patterns
324
+
325
+
Some possibilities:
326
+
327
+
- "Guard pattern" will likely be most intuitive to users already familiar with match arm guards. Most likely, this includes anyone reading this, which is why this RFC uses that term.
328
+
- "`if`-pattern" agrees with the naming of or-patterns, and obviously matches the syntax well. This is probably the most intuitive name for new users learning the feature.
329
+
- Some other possibilities: "condition/conditioned pattern," "refinement/refined pattern," "restriction/restricted pattern," or "predicate/predicated pattern."
330
+
331
+
[future-possibilities]: #future-possibilities
332
+
333
+
# Future Possibilities
334
+
335
+
## Allowing `if let`
336
+
337
+
Users expect to be able to write `if let` where they can write `if`. Allowing this in guard patterns would make them significantly more powerful, but also more complex.
338
+
339
+
One way to think about this is that patterns serve two functions:
340
+
341
+
1. Refinement: refutable patterns only match some subset of a type's values.
342
+
2. Destructuring: patterns use the structure common to values of that subset to extract data.
354
343
355
-
This would also give the guard ownership of the bound value, which may be desirable in other cases.
344
+
Guard patterns as described here provide _arbitrary refinement_. That is, guard patterns can match based on whether any arbitrary expression evaluates to true.
345
+
346
+
Allowing `if let` allows not just arbitrary refinement, but also _arbitrary destructuring_. The value(s) bound by an `if let` pattern can depend on the value of an arbitrary expression.
0 commit comments