rust-lang / rust

Empowering everyone to build reliable and efficient software.
https://www.rust-lang.org
Other
96.07k stars 12.41k forks source link

Tracking issue for RFC 2294, "if let guard" #51114

Open Centril opened 6 years ago

Centril commented 6 years ago

This is a tracking issue for the RFC "if let guard" (rust-lang/rfcs#2294).

Steps:

Unresolved questions:

imjacobclark commented 5 years ago

:wave: After a conversation about this change with @varkor I'm starting to take a look at this with a view to implementing it!

alexreg commented 5 years ago

@imjacobclark Hey. Any progress over the past few months? :-)

ebkalderon commented 5 years ago

Just ran into a case where this ability would be useful. I look forward to this feature landing someday!

Vincent-liuwingsang commented 5 years ago

+1 This would be a nice feature to have!

Silhalnor commented 5 years ago

Am currently learning Rust and thought it was unintuitive that I couldn't write this syntax. (Thought I must be mis-writing it somehow.) Lo, here it is in progress! Woo~

Boscop commented 4 years ago

Any update on this? :)

LeSeulArtichaut commented 4 years ago

I was willing to take a look at this, but I didn't have time to... I'll try to free some time for this ASAP!

LeSeulArtichaut commented 3 years ago

Currently looking into this, though I'm not sure I will be able to implement all of it

LeSeulArtichaut commented 3 years ago

Status update: the first implementation of this (#79051) is now available in nightly under #![feature(if_let_guard)].

joshtriplett commented 3 years ago

@LeSeulArtichaut Thanks for the implementation!

@nikomatsakis @pnkfelix This has an open question regarding the exact semantics and borrow handling. Is that question still open?

pnkfelix commented 3 years ago

The only semantics/borrowck question I'm aware of is the one written in the RFC itself:

What happens if guard_expr moves out of pat but fails to match? This is already a question for if guards and (to the author's knowledge) not formally specified anywhere — this proposal (implicitly) copies that behavior.

But as the question itself states, the issues of supposed "moves" during guard_expr already arises for if guards, and the proposal says it implicitly copies whatever behavior if guards use. So the main reason I think it was an unresolved question is that the RFC author did not feel comfortable attempting to codify the behavior, especially at the time that RFC was written (January 2018).

Why would someone feel uncomfortable doing so at that time? Because I think at that time we were still in the process of fixing the semantics of match guards, which were originally unsound because they would move out of patterns. (We fixed this by making each match guard stop moving out of the patterns for their respective arm). I'm still looking for the relevant issue/write-up here; I'll link it when I find it.

LeSeulArtichaut commented 3 years ago

IIUC if let guards as currently implemented should use the same semantics and code as if guards wrt moving out of bindings. Here's an example:

#![feature(if_let_guard)]

struct NotCopy;

fn main() {
    match Some(NotCopy) {
        Some(x) if let () = drop(x) => {}
        _ => {}
    }
}
error[E0507]: cannot move out of `x` in pattern guard
 --> src/main.rs:7:34
  |
7 |         Some(x) if let () = drop(x) => {}
  |                                  ^ move occurs because `x` has type `NotCopy`, which does not implement the `Copy` trait
  |
  = note: variables bound in patterns cannot be moved from until after the end of the pattern guard
pnkfelix commented 3 years ago

In any case, the main next step in the path to stabilization is as given in the description: "Adjust documentation."

Anyone who is interested in helping get this stabilized and who feels reasonably comfortable with writing technical documentation: reach out to me or another member of the lang team, and we'll see what we can do to walk you through how to update the documentation here.

pnkfelix commented 3 years ago

I wrote:

Why would someone feel uncomfortable doing so at that time? Because I think at that time we were still in the process of fixing the semantics of match guards, which were originally unsound because they would move out of patterns. (We fixed this by making each match guard stop moving out of the patterns for their respective arm). I'm still looking for the relevant issue/write-up here; I'll link it when I find it.

There's a bunch of answers. The most relevant one is probably: https://github.com/rust-lang/rfcs/pull/107 and https://github.com/rust-lang/rust/issues/15287#issuecomment-489308359

(There were also a host of related bugs, such as #1006, #24535, #27282, #29723, #31287.)

pnkfelix commented 3 years ago

And the most obviously relevant regression test was added in PR #47549

JakubKoralewski commented 2 years ago

As a nightly user, can I safely use this feature despite the incompleteness? I see the relevant PR to mark as complete, but it has not been merged as of yet.

LeSeulArtichaut commented 2 years ago

@JakubKoralewski I think this feature should be considered like any new feature, which hasn't been well tested yet and might have its slew of bugs. PR #87937 which makes if_let_guard no longer an incomplete feature has been approved and should be merged soon. Also #88066 is using if-let guards in the compiler.

pnkfelix commented 2 years ago

IIUC if let guards as currently implemented should use the same semantics and code as if guards wrt moving out of bindings. Here's an example: [...]

@LeSeulArtichaut I think I agree with the example you have provided.

But I skimmed over the rustc test suite a couple different ways, and I did not see any tests of that error case expressed using if let guards on match; only using if guards.

I think we should:

not-jan commented 2 years ago

Is this behavior intended? It breaks the analogous behavior of if guards by only applying the condition to the last pattern in the compound pattern.

https://play.rust-lang.org/?version=nightly&mode=debug&edition=2021&gist=a29997b8823091e21b8470d6751ebc3f

LeSeulArtichaut commented 2 years ago

@not-jan That is indeed a bug, see issue #88015

joshtriplett commented 1 year ago

Looks like https://github.com/rust-lang/rust/issues/88015 is the main blocker for this, along with ensuring sufficient test coverage and documentation. Are there any other known blockers?

semiviral commented 1 year ago

With #88015 having been resolved, are we currently waiting only for final documentation to get this feature into the FCP? I would be willing to provide a documentation PR for this.

LeSeulArtichaut commented 1 year ago

@semiviral I think adding more tests for if-let guards would be greatly appreciated. If you want to help, you might want to take a look a the test directory and add any test you might find interesting. Examples from the top of my head include testing let chains in if-let guards and making sure that the diagnostics for borrow-checking violations in if-let guards are correct and helpful (and, of course, that if-let guards are sound!). Every bit helps, so feel free to open a PR even if it's just a few additional examples.

On the topic of documentation, I had opened https://github.com/rust-lang/reference/pull/1082 to document if-let guards in the Reference. Adding an entry in Rust by Example might be helpful as well.

Antikyth commented 1 year ago

This is a feature which I have been using because it helps to make certain areas of my code, particularly when it comes to working with proc-macros and the very nested nature of syntax.

I haven't contributed to the Rust project before, but I would like to do so to give back to Rust, to develop my skills, and to perhaps see if I can be of help to features such as this which are useful to me.

I've been reading through the Guide to Rustc Development to become more familiar with the processes related to contributing and with the compiler. My current most technical experience with Rust would be developing proc macros, and I also enjoy documenting my code thoroughly. I'm wondering what areas of this feature I might be able to help with.

I see that it has been mentioned that the main blockers for this feature are documentation and tests. Could anyone suggest any areas for tests and documentation in particular would benefit from being looked at? As this is my first time looking at the compiler really (but I am a fast learner, I tend to learn quickly getting my hands dirty), I don't yet have a good notion of how to look for areas that need to be improved.

Edit: I can see that if let guards are not documented in the unstable book. I suppose this is something that needs to be done?

LeSeulArtichaut commented 1 year ago

Hi @Antikyth, thanks for the help! Here is, to my knowledge, the status of the work on if-let guards:

If you are interested in writing some tests or the Rust by Example chapter, please go ahead! Thanks again for your help.

I can see that if let guards are not documented in the unstable book. I suppose this is something that needs to be done?

I don't think that's particularly useful in the perspective of stabilization, but I might be wrong.

Antikyth commented 1 year ago

Hi @Antikyth, thanks for the help! Here is, to my knowledge, the status of the work on if-let guards:

If you are interested in writing some tests or the Rust by Example chapter, please go ahead! Thanks again for your help.

I can see that if let guards are not documented in the unstable book. I suppose this is something that needs to be done?

I don't think that's particularly useful in the perspective of stabilization, but I might be wrong.

Ah, thanks for the reply! I'll have a look at:

I may or may not get somewhere with those, will have to see if (a) I happen to have time to do so, (b) I can do something useful. But I'm happy to give it a try :)

matthewjasper commented 1 year ago

Is the code here supposed to work? I would expect only direct use of a let expression (or maybe a chain with && if if let chains is also enabled) to be accepted.

https://github.com/rust-lang/rust/blob/master/compiler/rustc_mir_build/src/build/expr/as_operand.rs#L121

edit: Looks like it's not intended to be allowed (and was fixed by #111841)

dtolnay commented 8 months ago

I opened https://github.com/rust-lang/rust/pull/118726 with a pretty-printer fix that is relevant to "if let guard". The rules for when struct literals must be enclosed in parentheses are different between if-let-guard and ordinary if-let.

#![feature(if_let_guard)]

macro_rules! stringify_expr {
    ($expr:expr) => {
        stringify!($expr)
    };
}

macro_rules! stringify_if_let {
    ($expr:expr) => {
        stringify_expr!(if let _ = $expr {})
    };
}

macro_rules! stringify_if_let_guard {
    ($expr:expr) => {
        stringify_expr!(match () { _ if let _ = $expr => {} })
    };
}

fn main() {
    println!("{}", stringify_if_let!(Struct {}));
    println!("{}", stringify_if_let_guard!(Struct {}));
}

stringify! must add parentheses around Struct {} in the if-let case, but it is not necessary to do so in the if-let-guard case because parsing of the expression is not terminated by { in that position.

if let _ = (Struct {}) {}
match () { _ if let _ = Struct {} => {} }