-
Notifications
You must be signed in to change notification settings - Fork 1.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
propose if let guard #2294
propose if let guard #2294
Conversation
Linking other control-flow proposals with varying degrees of relatedness: |
(For reference: in Haskell 2010, this same feature is called "pattern guards".) |
Just going by the examples in the RFC, I find it very difficult to tell where the match block, match arms, guards, etc all start and end (without stopping to consciously reason about it, which imo you shouldn't have to do for "basic" syntax like let/match/if/else). I think that's because these if let guards are significantly longer than everything else in the match block, and maybe that's not representative of whatever "real world usage" of this feature would be like, but it does give me a very strong first impression that this is feature is "going too far" and code would only very rarely be improved by it. @strake do you have any (presumably longer) code samples where the this feature would enable a clear improvement? |
This would close #2214 which is exactly this feature request. It's a natural extension to |
I don't see any way to desugar it into existing Rust constructions (at AST/HIR level), unfortunately. (First, every non-pattern condition if expr1 is pat1(bindings1) && ... && exprN is patN(bindingsN) {
stmts1
} else {
stmts2
} => match expr1 {
pat1(bindings1) => {
if expr2 is pat2(bindings2) && ... && exprN is patN(bindingsN) {
stmts1
} else {
goto ELSE
}
}
_ => goto ELSE
}
goto END:
ELSE:
stmts2
END: In this case For condition chains in "guard" match expr {
pat if expr1 is pat1(bindings1) && ... && exprN is patN(bindingsN) => {
stmts
}
_ => {}
} => match expr {
pat => match expr1 {
pat1(bindings1) => {
if expr2 is pat2(bindings2) && ... && exprN is patN(bindingsN) {
stmts
} else {
goto NEXT_ARM
}
}
_ => goto NEXT_ARM
}
NEXT_ARM:
_ => {}
} In this case we can't do it in existing Rust because we cannot attach labels to |
I wonder if labeled I can imagine them being potentially useful in the "common tail" situation for example: 'end: match expr {
pat1 => {
stmts1;
break 'common
}
pat2 => {
stmts2;
break 'common
}
pat3 => {
stmts3;
// no common
}
_ => break 'end,
'common: _ => {
common_stmts
}
} or with complex arm conditions match expr {
pat1 => {
complex_stmts;
if complex_condition {
// It was a mistake to go into this arm after all, let's try something else
break: 'next
}
}
'next: pat2 => {}
_ => {}
} |
cc #680 |
@Ixrec i have been writing code to avoid needing this feature, so i have none ready, but could try to write one. @petrochenkov on desugaring: my thought was we could rewrite an match x {
A(a) if let Some(y) = f_a(a) => g(y),
B(b) => f_b(b),
C(c) => f_c(c),
} vs match x {
A(a) => if let Some(y) = f_a(a) { g(y) } else match A(a) {
B(b) => f_b(b),
C(c) => f_c(c),
},
B(b) => f_b(b),
C(c) => f_c(c),
} |
@Ixrec I made another example; is it better? |
text/0000-if-let-guard.md
Outdated
# Motivation | ||
[motivation]: #motivation | ||
|
||
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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
„iff“ -> „if“ typo or is this some sort of abbreviation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
iff means if and only if to distinguish between logical implication p -> q
or logical equivalence p <-> q
(which is equivalent to p -> q && q -> p
). Could be written out to make it clearer for those not familiar with this particular mathematical jargon.
The Lang Team discussed this RFC today, with the following take-aways:
Overall, the consensus was that we should move forward with this RFC, but with a clearly provisional flavor (as with other recent syntactic additions): when it comes time to stabilize, we want to take a holistic look at the way the syntax is evolving across these RFCs. @rfcbot fcp merge |
Team member @aturon has proposed to merge this. The next step is review by the rest of the tagged teams: Concerns:
Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
I have 2 concerns:
match x {
'C' if let &[n] = parms =>
self.screen.move_x( n as _), // note the linebreak!
...
} or if the bit before match ui.wait_event() {
KeyPress(mod_, key, datum)
if let Some(action) = intercept(mod_, key) => act(action, datum),
ev => accept!(ev),
}
if let A(x) = foo()
if let B(y) = bar()
if cond(x, y) {
...
} This ^ particular syntax was not (iirc) very intelligible in the survey done at #2260. The RFC does write that you can't chain |
@Centril What consistency between |
@strake you would not be permitted to chain an |
I see a majority of boxes checked — what is holding back FCP? |
@rfcbot concern borrowck We are currently working through how to rationalize our existing patterns in MIR borrowck, and in particular addressing a number of soundness complications. It's not entirely obvious to me how this RFC is going to interact with that -- for example, the current treatment that we are moving towards is that -- in a guard expression -- the bindings from the pattern are compiled into refrences into the value being matched, with an implicit deref. So e.g. in a match like this: match foo {
Some(x) if x > 5 => {..}
} although the type of I feel like before we accept this RFC, we ought to talk through what its MIR desugaring will be and how it will interact with the borrow check. (cc @pnkfelix) |
@nikomatsakis and @pnkfelix need to discuss more. But the executive summary is: @pnkfelix currently believes that there is a relatively straight-forward semantics under the It might be a good idea to try to work through those semantics, perhaps via a series of examples, within this RFC text. Or it might be a good idea to just accept this RFC, with the understanding that we will need to work out the exact semantics (in terms of when the various bindings occur in the MIR desugaring) as part of the implementation effort. |
@rfcbot resolve borrowck OK, so it seems I killed the conversation with my comment. I apologize that the comment didn't make it particularly clearly what sort of follow-up would satisfy me. After some consideration, I've decided to withdraw my concern. It's not that I'm not still worried, it's that I think it's ok to merge this RFC basically as our "stated intention" to specify this more clearly. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
I think that this sounds good: match keyinfo {
/* Separate 'if let' with 'if' using '&&'
note: && is parsed regularly after initial && separator */
Some(keyinfo) if let Some(key) = get_key(keyinfo) && key == K_ENTER && key == K_UP || key == K_DOWN => {
// stuff
},
_ => {
// other stuff
}
} |
The final comment period, with a disposition to merge, as per the review above, is now complete. |
Huzzah! This RFC is merged. Tracking issue: rust-lang/rust#51114 |
…thewjasper Implement if-let match guards Implements rust-lang/rfcs#2294 (tracking issue: rust-lang#51114). I probably should do a few more things before this can be merged: - [x] Add tests (added basic tests, more advanced tests could be done in the future?) - [x] Add lint for exhaustive if-let guard (comparable to normal if-let statements) - [x] Fix clippy However since this is a nightly feature maybe it's fine to land this and do those steps in follow-up PRs. Thanks a lot `@matthewjasper` ❤️ for helping me with lowering to MIR! Would you be interested in reviewing this? r? `@ghost` for now
…thewjasper Implement if-let match guards Implements rust-lang/rfcs#2294 (tracking issue: rust-lang#51114). I probably should do a few more things before this can be merged: - [x] Add tests (added basic tests, more advanced tests could be done in the future?) - [x] Add lint for exhaustive if-let guard (comparable to normal if-let statements) - [x] Fix clippy However since this is a nightly feature maybe it's fine to land this and do those steps in follow-up PRs. Thanks a lot `@matthewjasper` ❤️ for helping me with lowering to MIR! Would you be interested in reviewing this? r? `@ghost` for now
Implement if-let match guards Implements rust-lang/rfcs#2294 (tracking issue: #51114). I probably should do a few more things before this can be merged: - [x] Add tests (added basic tests, more advanced tests could be done in the future?) - [x] Add lint for exhaustive if-let guard (comparable to normal if-let statements) - [x] Fix clippy However since this is a nightly feature maybe it's fine to land this and do those steps in follow-up PRs. Thanks a lot `@matthewjasper` ❤️ for helping me with lowering to MIR! Would you be interested in reviewing this? r? `@ghost` for now
Rendered
Tracking issue
I find myself wanting this often (the latest use case being parsing terminal escape codes).