Skip to content
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

if-let patterns should be as expressive as match patterns #935

Closed
Munksgaard opened this issue Mar 4, 2015 · 25 comments
Closed

if-let patterns should be as expressive as match patterns #935

Munksgaard opened this issue Mar 4, 2015 · 25 comments
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@Munksgaard
Copy link

Right now, you can do this

enum Foo {
  One(u8),
  Two(u8),
  Three
}
use Foo::*;

fn main () {
    let x = One(42);
    match x {
        One(n) | Two(n) => {
            println!("Got one or two with val: {}", n);
        }
        _ => {}
    }
}

but not this

enum Foo {
  One(u8),
  Two(u8),
  Three
}
use Foo::*;

fn main () {
    let x = One(42);
    if let One(n) | Two(n) = x {
        println!("Got one or two with val: {}", n);
    }
}

The same goes for pattern guards.

The RFC for if-let proposes the if-let notation pretty much as syntactic sugar for a match block with an empty arm at the end. However, if-let doesn't allow matching on pattern guards and multiple patterns. Extending the if-let notation to allow guards and multiple patterns feel like a natural extension to the current functionality with plenty of use cases.

I've started on an actual rfc, but there might be some parsing and syntactic details we'd have to figure out before it makes sense to finish it. For instance, if let Some(n) = x if n = 0 looks weird at best.

On the positive side, adding support for guards and multiple patterns shouldn't break anything.

cc @Manishearth

@lifthrasiir
Copy link
Contributor

Actually this relates to another issue: Why don't we allow alternations (|) in any patterns, not just top-level patterns in match?

@Manishearth
Copy link
Member

@Munksgaard
Copy link
Author

@lifthrasiir It certainly does, but I feel like many of the issues that pertain to #99 don't apply here, since if-let is really just sugaring for a match statement.

@Manishearth
Copy link
Member

I guess the only changes we need here are to allow | in the grammar and extend if let parsing to produce ExprMatches with multiple Pats per arm. No change to the AST required (yay).

@Manishearth
Copy link
Member

My bad, we'll need to make ExprIfLet take a Vec<Pat>

@Munksgaard
Copy link
Author

This branch adds support for multiple patterns in both if-let and while-let statements. If we want to do the same for guards, we'll probably need to discuss what syntax we want.

https://github.com/Munksgaard/rust/tree/if-while-let-multiple-patterns

@Manishearth
Copy link
Member

I don't think if let should support guards. if let Some(a) == bar && baz() {} seems like a reasonable syntax however. But I'm wary of supercharging if let, it's just a sugar after all.

@Munksgaard
Copy link
Author

I don't really see any other reasons beside the syntax for not letting if let support guards. As you said, it's just sugar, so any guards would translate directly into a match guard.

As you're saying though, the syntax could get really ugly.

Wouldn't if let Some(a) == bar && baz() {} introduce ambiguity? For instance: if let true = x && false { }

@Manishearth
Copy link
Member

Yep. Another reason to avoid it :)

@pcwalton
Copy link
Contributor

pcwalton commented Mar 4, 2015

I've wanted guards in if let multiple times. This is an easy backwards compatible change. +1 from me.

For some reason while let ... if ... { ... } strikes me as a little brain-twisting at first, but the semantics are clear.

@Munksgaard
Copy link
Author

@pcwalton:
Yeah, I'm mostly for that syntax as well, no ambiguities, even though it's a bit of an eyesore.

@mdinger
Copy link
Contributor

mdinger commented Mar 5, 2015

#929 is similar to this bug but it focuses on on adding && instead of |. It uses || because of similarity to if A || B although it was only minor because I thought || might be more unlikely to be accepted.

@mdinger
Copy link
Contributor

mdinger commented Mar 5, 2015

My last comment was a mistake. They do look slightly similar but they are completely different types of proposals.

@ahicks92
Copy link

What is the status of this, and is this the latest discussion?

I want to talk about this as someone who is new to Rust but has significant experience with C++ and has played with Haskell. Rust sold itself to me before I had even written a line.

I read about patterns in the book. Then I read about if let in the book, and it doesn't mention any restrictions. As someone coming into the language, this feels like a bug or oversight, not something that should have an RFC.

I do agree that the syntax is ugly, but I don't see it as any less ugly than the corresponding match for the same thing. The if let has the pattern guard, but the match has the second arm, the pattern guard, and an additional level of nesting.

If there is some obvious reason that this shouldn't work as one would expect, I'd love to know what it is. A lot of my annoyance with learning Rust has been mitigated because I can look at it and see the obvious reason. But I can't here, and it doesn't exist judging by the other comments on this issue. I would expect that a pattern is always the same thing and following the same rules, not that it's sometimes different.

@icorderi
Copy link

I just run into this, was surprised I couldn't use a guard in the if-let.

This looks very weird and wouldn't have expected it to compile:
if let <pattern> = .. if x > 3 { /* ... */ }

But I was expecting the following to work: if let ... && <cond> { /* ... */ }

@Diggsey
Copy link
Contributor

Diggsey commented Aug 28, 2016

I've run into this several times as well. Can't we just have a single syntax for a "pattern" which includes multiple cases, guards, etc. and is used for match, if let and while let. This adds a useful feature while also simplifying the language.

@nagisa
Copy link
Member

nagisa commented Aug 28, 2016

That sort of pat does not make sense in the context of arguments (e.g. fn
peach(a if a > 0: i32), though.

On Aug 28, 2016 9:36 PM, "Diggory Blake" notifications@github.com wrote:

I've run into this several times as well. Can't we just have a single
syntax for a "pattern" which includes multiple cases, guards, etc. and is
used for match, if let and while let. This adds a useful feature while
also simplifying the language.


You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
#935 (comment), or mute
the thread
https://github.com/notifications/unsubscribe-auth/AApc0r1dlgpwIJAQEI9ZgxGqK4HNB4hFks5qkdU0gaJpZM4DpR5H
.

@Diggsey
Copy link
Contributor

Diggsey commented Aug 28, 2016

@nagisa That's simply the difference between refutable and irrefutable patterns. Function args must of course be irrefutable, unless you allow multiple function definitions to cover all possible cases.

@nrc nrc added the T-lang Relevant to the language team, which will review and decide on the RFC. label Aug 29, 2016
@SoniEx2 SoniEx2 mentioned this issue Jan 17, 2018
@Centril
Copy link
Contributor

Centril commented Mar 24, 2018

I the merged RFC #2175 covers this issue; so I'm closing it.

@teohhanhui
Copy link

the merged RFC #2175 covers this issue

But if let with guard is not covered...

@Rua
Copy link

Rua commented Jun 24, 2020

I have wanted this feature many times, it doesn't make sense for it to not be there.

@Twey
Copy link

Twey commented Jun 24, 2020

I don't understand why this was closed — there are more things missing from if let than just or-patterns. Can we re-open? Or does if let support all pattern features now?

@Manishearth
Copy link
Member

@Twey issues on the rfcs repo aren't really a "thing" either way, they're used inconsistently and we're moving away from that. Feel free to discuss this on internals.rust-lang.org and open an RFC

@fluffysquirrels
Copy link

To those that find this from a web search, here's the if-let-chains RFC that addresses conditions in if let expressions:

https://rust-lang.github.io/rfcs/2497-if-let-chains.html

@loganbnielsen
Copy link

I'm assuming while let fits in the same bucket. Would be nice to be able to do something like

while let Some(result_byte) if result_byte != b'"' = byte_stream.next() {

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests