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
First, there are currently 474 issues|PRs here that contain "if let", so I hope I'm not duplicating something already discussed.
This idea is quite close to #929, but instead of allowing to &&lets, it'd allow to && with a bool.
Basically, the idea looks like this:
ifletSome(x) = y && x > 42{println!("foo");}else{println!("bar");}// Would be equivalent to:ifletSome(x) = y {if x > 42{println!("foo");}else{println!("bar");}}else{println!("bar");}
This pattern is something that frequently occurred to me, and I'm finding it painful to always look for workarounds.
The constraints to this pattern would be:
Only && could be used after a let, especially if a || is wanted it must be as a child node of the && (required so that the RHS could be evaluated with the let binding in scope)
For the time being, it is restricted to one let and one or more && with bools, leaving ideas like if let Some((a, b)) = x && a > 42 && let Some(c) = b.unwrap() for later (modulo Support && in if let expressions #929)
What do you think about this idea?
Reacted by Nadrieril, Kornel, shouya, Dwayne Slater, kgtkr, Yuze Fu, BitOfCoffee, Miguel Saldivar, Arash Sahebolamri, 오웬 and 30 more
Reacted by Árpád Goretity and Velislava Yanchina
Reacted by Árpád Goretity , Kyle J Strand, pyfisch and CatCode79
RFC, survey by author showed very let Some(x) = y && x > 42 is the most popular syntax, yielded a long discussion but postponed: RFC: if- and while-let-chains #2260
There may be more.
(BTW, if the condition does only a single pattern match, a workaround can simply be a match with a guard. I think that's why multiple lets in if is especially demanded.)
match x {Some(x)if x > 42 => foo,
_ => bar,}
Reacted by Árpád Goretity , cybai, erin petra sofiya moon, Cory Sherman, wide-eyed wanderer, Orhun Parmaksız, jacksonriley, Kane Wang and Felipe S. S. Schneider
Well, I had come upon #2260, and that's the reason why I wanted to just stay with the simple if let [pat] = [var] && [cond], in order to keep things simple.
Actually, reading #2175 makes me think the syntax may be better like:
ifletSome(x) = y if x > 42{
foo
}else{
bar
}
This way 1/ the syntax is exactly the same as that of match guards, which is more consistent, and 2/ it avoids the issue of having to set an operator precedence to let in if let true = x && false {.
However it's then going back into the debate of #2260, and I hoped a reduced scope might help in getting at least something? Or maybe I should just wait for things to settle a bit and then @petrochenkov's implementation will help things move forward, despite the RFC being closed?
Reacted by Eira Fransham, Cory Sherman, Sebastian Malton, Marcin Wojnarowski and Juan Abadie
Hey there. Me and @scottmcm wrote #2260. Since then #2046 has been merged and is getting implemented soon (by @est31).
If you want to propose if let [pat] = [expr] && [expr], which is my preferred syntax, then you should consider how you deal with any potential ambiguities. The [expr] is [pat] syntax that @petrochenkov has been working on hasn't been RFCed yet, so we still need to discuss the design and see if we want to go with [expr] is [pat] or not.
Overall, I think it'd likely be better to first RFC the [expr] is [pat] (if anything because there is already a tentative implementation of it), and depending on the result come back here.
About dealing with ambiguities, I think that in order to stay retro-compatible the only way might be to force parenthesizing the let block (as if let x = a && b already parses and has a meaning currently).
This would evaluate EXPR1 and try to match it with BINDING. If BINDING matches, then EXPR2 is evaluated with BINDING in scope. If EXPR2 evaluates to true, then STMT3 is run.
If any of the above fails, STMT4 is run.
The advantage of this syntax is that it could, later, if need arises, be extended to an arbitrary sequence of let and expressions in turn. (not trying to include those for now as it's likely better to stay with a conservative syntax -- I was even thinking of enforcing putting parenthesis around EXPR2, but I think the paragraph below handles this quite well too)
However, in order to not introduce unexpected things (like if (let a = 3) && a == 3 || b == 2 which would parse despite operator precedence making || theoretically the highest-level item of the AST -- but not with the proposed syntax), an additional rule should be placed stating that in EXPR2, the top-level node of the AST must be of a precedence stronger than or equal to the one of &&.
Additionally, depending on the current usage among crates, a warning could be put in when doing if let a = b && c, as it'd be parsed as b && c matched into a: this could take the form of a warning put forward when parsing an if let, if the RHS of the match has a top-level AST node of && and is not parenthesized.
With this I think we should have make a run around the potential issues of syntax put forward by this proposal.
The [expr] is [pat] syntax that @petrochenkov has been working on hasn't been RFCed yet,
Overall, I think it'd likely be better to first RFC the [expr] is [pat]
There are some holidays in May, so I hope to write an EXRP is PAT RFC based on the implementation experience.
After "tasting" the feature while porting rustmt to my experimental branch I now really miss it while writing normal Rust code accepted by the standard compiler.
Reacted by Léo Gaspard, F001, Jason Priest, I60R and Nico Orrù
I'm not sure about overloading && in this way. As nice as it is to have, I view if let already as kind of hacky syntax, and overloading && to work on pattern expressions in a really niche way only compounds the problem.
I think is is better if I'm understanding it correctly, though if this supports && I'm still not a fan of declaring and using something in the same header:
if foo is Some(x) && bar is Some(y) && x > y { }
Is perhaps clear, but the semantics are a bit weird. It's much better than if (x = foo()) in C, for certain, but something about allowing && (and ||) to double duty pattern matches as boolean expressions AND bindings which occur in the same loop header, and also have left-to-right scoping is a bit head spinning when I try and think about it.
To be honest, we could use "where" for this:
if x < y where foo is Some(x), bar is Some(y) {
}
Extends to if/else blocks:
if x < y {
} else if x > y {
} else {
} where foo is Some(x), bar is Some(y)
or possibly even allowing different decompositions via if else (rather than forcing the user into match):
if x < y where foo is Some(x), bar is Some(y) {
} else if x > 5 && z < 10 where foo is Some(x), baz is Some(z) {
}
// ...
Another option if you prefer forcing users to declare their variables before use rather than needing to defer name resolution is something like "st" for "such that"
if foo is Some(x), bar is Some(y) st x < y {
}
I feel like these more neatly communicate a separation of ideas as far as declaring bindings vs testing conditions on those bindings. While it's true the binding is itself somewhat a condition, personally I feel they're sufficiently different to warrant syntactic separation as well.
The only major potential drawback I see to this approach over if foo is Some(x) && x > 5 style is losing short circuiting power (if foo is Some(x) && x > 5 && some_expensive_function() is Some(y)), but personally I'm not sure burying expensive calls behind short circuits is the best style anyway.
I can't say that the is syntax feels particularly appealing, not least of which because it reverses the order of the pattern and the expression to match against it. I prefer the proposal of parenthesizing the let.
Activity
sinkuu commentedon Apr 21, 2018
Actually this seems to have been discussed many times:)
if y is Some(x) && x > 42): Support && in if let expressions #929 (comment)let Some(x) = y && x > 42is the most popular syntax, yielded a long discussion but postponed: RFC: if- and while-let-chains #2260(BTW, if the condition does only a single pattern match, a workaround can simply be a match with a guard. I think that's why multiple
lets inifis especially demanded.)Ekleog commentedon Apr 21, 2018
Well, I had come upon #2260, and that's the reason why I wanted to just stay with the simple
if let [pat] = [var] && [cond], in order to keep things simple.Actually, reading #2175 makes me think the syntax may be better like:
This way 1/ the syntax is exactly the same as that of match guards, which is more consistent, and 2/ it avoids the issue of having to set an operator precedence to
letinif let true = x && false {.However it's then going back into the debate of #2260, and I hoped a reduced scope might help in getting at least something? Or maybe I should just wait for things to settle a bit and then @petrochenkov's implementation will help things move forward, despite the RFC being closed?
Centril commentedon Apr 21, 2018
Hey there. Me and @scottmcm wrote #2260. Since then #2046 has been merged and is getting implemented soon (by @est31).
If you want to propose
if let [pat] = [expr] && [expr], which is my preferred syntax, then you should consider how you deal with any potential ambiguities. The[expr] is [pat]syntax that @petrochenkov has been working on hasn't been RFCed yet, so we still need to discuss the design and see if we want to go with[expr] is [pat]or not.Ekleog commentedon Apr 21, 2018
Overall, I think it'd likely be better to first RFC the
[expr] is [pat](if anything because there is already a tentative implementation of it), and depending on the result come back here.About dealing with ambiguities, I think that in order to stay retro-compatible the only way might be to force parenthesizing the
letblock (asif let x = a && balready parses and has a meaning currently).So I'd see it like this:
This would evaluate
EXPR1and try to match it with BINDING. If BINDING matches, then EXPR2 is evaluated with BINDING in scope. If EXPR2 evaluates totrue, then STMT3 is run.If any of the above fails, STMT4 is run.
The advantage of this syntax is that it could, later, if need arises, be extended to an arbitrary sequence of
letand expressions in turn. (not trying to include those for now as it's likely better to stay with a conservative syntax -- I was even thinking of enforcing putting parenthesis aroundEXPR2, but I think the paragraph below handles this quite well too)However, in order to not introduce unexpected things (like
if (let a = 3) && a == 3 || b == 2which would parse despite operator precedence making || theoretically the highest-level item of the AST -- but not with the proposed syntax), an additional rule should be placed stating that inEXPR2, the top-level node of the AST must be of a precedence stronger than or equal to the one of&&.Additionally, depending on the current usage among crates, a warning could be put in when doing
if let a = b && c, as it'd be parsed asb && cmatched intoa: this could take the form of a warning put forward when parsing anif let, if the RHS of the match has a top-level AST node of&&and is not parenthesized.With this I think we should have make a run around the potential issues of syntax put forward by this proposal.
petrochenkov commentedon Apr 21, 2018
There are some holidays in May, so I hope to write an
EXRP is PATRFC based on the implementation experience.After "tasting" the feature while porting rustmt to my experimental branch I now really miss it while writing normal Rust code accepted by the standard compiler.
UserAB1236872 commentedon Apr 28, 2018
I'm not sure about overloading
&&in this way. As nice as it is to have, I viewif letalready as kind of hacky syntax, and overloading&&to work on pattern expressions in a really niche way only compounds the problem.I think
isis better if I'm understanding it correctly, though if this supports&&I'm still not a fan of declaring and using something in the same header:Is perhaps clear, but the semantics are a bit weird. It's much better than
if (x = foo())in C, for certain, but something about allowing&&(and||) to double duty pattern matches as boolean expressions AND bindings which occur in the same loop header, and also have left-to-right scoping is a bit head spinning when I try and think about it.To be honest, we could use "where" for this:
Extends to if/else blocks:
or possibly even allowing different decompositions via
if else(rather than forcing the user into match):Another option if you prefer forcing users to declare their variables before use rather than needing to defer name resolution is something like "st" for "such that"
I feel like these more neatly communicate a separation of ideas as far as declaring bindings vs testing conditions on those bindings. While it's true the binding is itself somewhat a condition, personally I feel they're sufficiently different to warrant syntactic separation as well.
The only major potential drawback I see to this approach over
if foo is Some(x) && x > 5style is losing short circuiting power (if foo is Some(x) && x > 5 && some_expensive_function() is Some(y)), but personally I'm not sure burying expensive calls behind short circuits is the best style anyway.F001 commentedon May 3, 2018
I agree with @LinearZoetrope that the combination of
is&&||is weird. What about this:crlf0710 commentedon May 3, 2018
The
&&part in C++ is;. But I guess Rust doesn't have to be the same though.joshtriplett commentedon May 4, 2018
I can't say that the
issyntax feels particularly appealing, not least of which because it reverses the order of the pattern and the expression to match against it. I prefer the proposal of parenthesizing the let.Centril commentedon Oct 7, 2018
Closing this since #2497 was accepted.
Ekleog commentedon Oct 7, 2018
If I may, I think there's been a communication issue here: this issue should have been pinged when the eRFC was open. Now… it does look good, thanks!
davidrolle commentedon Jan 16, 2020
While they might sound similar, #2497 actually has no overlap with this issue:
if let Some(x) = foo && let Some(y) = barif let Some(x) = foo && x > 42Therefore I believe this issue should stay open.
As a side note, we should consider including the change to
while letas well and not onlyif letkennytm commentedon Jan 16, 2020
@davidrolle #2497 already included
if let Some(x) = foo && x > 42andwhile let &&. Please check the RFC text in additional to the summary.