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

Explicit refutable let: let..else #373

Closed
glaebhoerl opened this issue Oct 8, 2014 · 13 comments
Closed

Explicit refutable let: let..else #373

glaebhoerl opened this issue Oct 8, 2014 · 13 comments
Labels
A-control-flow Proposals relating to control flow. A-expressions Term language related proposals & ideas A-syntax Syntax related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.

Comments

@glaebhoerl
Copy link
Contributor

Our lets are currently irrefutable. This is good. We could also have refutable lets with an explicit else clause:

let Ok(foo) = do_thing(...) else panic!();

In the simplest formulation, there would be a single else clause with the argument being anything of type ! (break, return, panic!, etc.). More sophistication is possible with chained else clauses, where multiple RHSs are tried in succession, with only the last one required to be of type !; and by also allowing a literal or constant which definitely matches rather than just an ! (so in the above example, else Ok(9), for instance).

The use case for this overlaps with if let, but not completely. if let involves rightward drift, and is appropriate if you want access to e.g. the value in an Option for a short period, and then to do other things afterwards, or if you want to handle the else case by something other than exiting. let..else avoids rightwards drift and is appropriate if you want to early-escape from the whole computation when the pattern doesn't match.

The potential for syntactic ambiguity with if..else needs to be thought through. But I don't believe there is an actual problem:

let PAT = if { EXPR } else { EXPR };

If we interpret this as being an if expression without an else block, and the else block as belonging to the let, that ends up being contradictory: if expressions have type (), which is never refutable, and so doesn't make any sense as the type of PAT in a refutable let. So the only sensible way to interpret this is as an if-else expression on the RHS.

Earlier discussion on the mailing list.

@pnkfelix
Copy link
Member

pnkfelix commented Oct 8, 2014

I don't like the idea of having to scan ahead in the program text for else in order to tell whether I should expect the pattern to be irrefutable or not.

Is there some way we could revise our if let semantics to cover this case as well? Or use some other token sequence that shows up at the beginning of the expression form, rather than overloading let in this way?

@pnkfelix
Copy link
Member

pnkfelix commented Oct 8, 2014

(also, I'm worried about the RFC repo issue database creeping into http://discuss.rust-lang.org/ territory ... if you are unhappy with the discuss interface, or think that it is not a good area for proposing sketches of language features, then we should discuss that and try to address it.)

@sinistersnare
Copy link

I agree, this is the wrong place for "ideas", something like this belongs in the discourse forum.

@glaebhoerl
Copy link
Contributor Author

I don't like the idea of having to scan ahead in the program text for else in order to tell whether I should expect the pattern to be irrefutable or not.

Most of the time this should be apparent from the pattern itself, though.

I asked @aturon about that on IRC:

[22:14:55] aturon: anyways, what I came in here to ask is whether I gather correctly that it's okay to open issues in the rfcs repo for "things we eventually (probably) want to do" or "things which should have RFCs, but don't yet"
[22:15:11] glaebhoerl: yes, please do!

Perhaps I phrased my question, or interpreted the response incorrectly, but this in particular falls under the latter. I don't want to have a design discussion about this right now in preparation for an RFC (I intentionally want to "get it off my plate" so I can work on more important things); I just want there to be a public record of the idea where others can see it and it doesn't get lost. I feel that a GitHub issue is more appropriate for this than the forum.

(And in any case, if I have to expend more effort on thinking about where to post things than on the thing I want to post... feel free to move it.)

@aturon
Copy link
Member

aturon commented Oct 8, 2014

@glaebhoerl @pnkfelix

I think the policy on this is a work in progress (since we just started using this issue tracker a couple of weeks ago). We migrated a bunch of stuff from the rust repo over here that was along these lines, so I thought this would be fine too.

We've been having a bit of forum/process churn, so it sounds like the Rust Team needs to nail down (and write up) the policy for what goes where.

Anyway, sorry @glaebhoerl for the policy pushback; my fault.

@nrc
Copy link
Member

nrc commented Oct 8, 2014

Filed #375 for the meta-discussion.

@glaebhoerl
Copy link
Contributor Author

In the meantime Swift has added an analogous feature in the form of guard-else.

Another way to express this functionality which recently occurred to me would be to invert if let:

unless let Ok(result) = do_thing() { return }
println!("result = {}", result)

So where if let scopes the bound variables inside the block, unless let scopes them outside. This would probably also entail adding unless cond as an analogue for if !cond for normal conditionals as well, which seems more Perlish than Rustic, but I think unless let itself is kinda cool. (You could also formulate it as if !let instead of unless let of course, but that strikes me as much more weird and arcane.) Anyways, just brainstorming.

EDIT: This would also extend cleanly to until let - run the loop body until binding the pattern succeeds, upon which it becomes available to the remainder of the scope - although I'm not sure what the use case for that might be. Maybe something like CAS loops?

@fschutt
Copy link

fschutt commented Jan 12, 2018

Another idea would be something like this:

for i in 0..100 {
     let a = my_vec.get(i).unwrap_or(continue);
     let a = my_vec.get(i + 1).unwrap_or(return);
}

or:

for i in 0..100 {
     let a = my_vec.get(i).unwrap_or({ continue });
}

Meaning, the thing in the "unwrap_or" is an expression that gets evaluated (meaning "continue" in itself is an expression). Like "continue" being a value.

return could be a shorthand for "return ()". This would require that language-level items (such as return or continue) are allowed to be returned from a function.

Just an idea.

@Centril Centril added the T-lang Relevant to the language team, which will review and decide on the RFC. label Jan 12, 2018
@Wyverald
Copy link

Is there an RFC related to this issue? I'd really love to see this feature.

@glaebhoerl
Copy link
Contributor Author

There was: #1303

@Centril Centril added A-syntax Syntax related proposals & ideas A-expressions Term language related proposals & ideas A-control-flow Proposals relating to control flow. labels Nov 27, 2018
wycats pushed a commit to wycats/rust-rfcs that referenced this issue Mar 5, 2019
@Fishrock123
Copy link
Contributor

Fishrock123 commented Jun 8, 2021

Modernized let-else RFC: #3137

@est31
Copy link
Member

est31 commented Sep 5, 2021

Should this be closed in favour of rust-lang/rust#87335 ?

@kennytm
Copy link
Member

kennytm commented Sep 6, 2021

closing as #3137 has been accepted.

@kennytm kennytm closed this as completed Sep 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-control-flow Proposals relating to control flow. A-expressions Term language related proposals & ideas A-syntax Syntax related proposals & ideas T-lang Relevant to the language team, which will review and decide on the RFC.
Projects
None yet
Development

No branches or pull requests