Open Centril opened 6 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!
@imjacobclark Hey. Any progress over the past few months? :-)
Just ran into a case where this ability would be useful. I look forward to this feature landing someday!
+1 This would be a nice feature to have!
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~
Any update on this? :)
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!
Currently looking into this, though I'm not sure I will be able to implement all of it
Status update: the first implementation of this (#79051) is now available in nightly under #![feature(if_let_guard)]
.
@LeSeulArtichaut Thanks for the implementation!
@nikomatsakis @pnkfelix This has an open question regarding the exact semantics and borrow handling. Is that question still open?
The only semantics/borrowck question I'm aware of is the one written in the RFC itself:
What happens if
guard_expr
moves out ofpat
but fails to match? This is already a question forif
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.
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
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.
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.)
And the most obviously relevant regression test was added in PR #47549
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.
@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.
IIUC
if let
guards as currently implemented should use the same semantics and code asif
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:
if
guard ones that test if let
guard insteadif
and if let
guards.[ ] (If there is no documentation of if
guards behavior w.r.t. move semantics, I do not think that should hold up stabilizing this feature. We can just file an issue that those things need to be documented.)
I'll add these work items to the list in the description.
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.
@not-jan That is indeed a bug, see issue #88015
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?
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.
@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.
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?
Hi @Antikyth, thanks for the help! Here is, to my knowledge, the status of the work on if-let guards:
tests/ui/rfc-2294-if-let-guard
) should be added to cover e.g. the interaction between if-let guards and let-chains? We might also want to encourage people on nightly to use the feature to collect feedback? If you noticed surprising behaviour or bad diagnostics, please open an issue!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.
Hi @Antikyth, thanks for the help! Here is, to my knowledge, the status of the work on if-let guards:
- There aren't any known bugs with the feature but testing is insufficient. More unit tests (found in
tests/ui/rfc-2294-if-let-guard
) should be added to cover e.g. the interaction between if-let guards and let-chains? We might also want to encourage people on nightly to use the feature to collect feedback? If you noticed surprising behaviour or bad diagnostics, please open an issue!- https://github.com/rust-lang/reference/pull/1082 is adding if-let guards to the Reference and should be good to go
- I opened https://github.com/rust-lang/rust-by-example/issues/1667 to ask if if-let guards should be included in Rust by Example
- Formatting if-let guards is blocked on a decision from the Style team (https://github.com/rust-lang/rustfmt/pull/5203), though that is not blocking stabilization
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 :)
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.
edit: Looks like it's not intended to be allowed (and was fixed by #111841)
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 {} => {} }
This is a tracking issue for the RFC "if let guard" (rust-lang/rfcs#2294).
Steps:
Unresolved questions:
if
guard ones that testif let
guard insteadif
andif let
guards.if
guards behavior w.r.t. move semantics, I do not think that should hold up stabilizing this feature. We can just file an issue that those things need to be documented.)let else
were implemented but had problems discovered relatively late with their handling of certain details w.r.t. locals and temporary r-values is a hint that we may need to revisit our approach to testing features like this more broadly, in terms of how we advise implementors of such features on how to probe for such cases (and maybe also in the form of some kind of language-oriented test-generation tooling?)