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 · 19 comments

Comments

Projects
None yet
@Munksgaard
Copy link

Munksgaard commented Mar 4, 2015

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

This comment has been minimized.

Copy link
Contributor

lifthrasiir commented Mar 4, 2015

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

@Manishearth

This comment has been minimized.

@Munksgaard

This comment has been minimized.

Copy link
Author

Munksgaard commented Mar 4, 2015

@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

This comment has been minimized.

Copy link
Member

Manishearth commented Mar 4, 2015

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

This comment has been minimized.

Copy link
Member

Manishearth commented Mar 4, 2015

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

@Munksgaard

This comment has been minimized.

Copy link
Author

Munksgaard commented Mar 4, 2015

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

This comment has been minimized.

Copy link
Member

Manishearth commented Mar 4, 2015

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

This comment has been minimized.

Copy link
Author

Munksgaard commented Mar 4, 2015

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

This comment has been minimized.

Copy link
Member

Manishearth commented Mar 4, 2015

Yep. Another reason to avoid it :)

@pcwalton

This comment has been minimized.

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

This comment has been minimized.

Copy link
Author

Munksgaard commented Mar 4, 2015

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

@mdinger

This comment has been minimized.

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

This comment has been minimized.

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.

@camlorn

This comment has been minimized.

Copy link

camlorn commented Apr 23, 2016

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

This comment has been minimized.

Copy link

icorderi commented May 24, 2016

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

This comment has been minimized.

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

This comment has been minimized.

Copy link
Contributor

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

This comment has been minimized.

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 label Aug 29, 2016

@stasm stasm referenced this issue Dec 20, 2017

Merged

0.5 comments #39

@SoniEx2 SoniEx2 referenced this issue Jan 17, 2018

Closed

Generic let #2297

@Centril

This comment has been minimized.

Copy link
Contributor

Centril commented Mar 24, 2018

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

@Centril Centril closed this Mar 24, 2018

@sinkuu sinkuu referenced this issue Apr 21, 2018

Closed

if let && bool #2411

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.