-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
RFC: throw
expressions
#2426
RFC: throw
expressions
#2426
Conversation
I guess I'll be the one to make the highly subjective but critical point against this, since I think we need to start gathering opinions on it ASAP if it's going to get addressed properly: I'm still not convinced that In fact, the motivation section for this RFC made me more skeptical that I know Also, the prior art section feels highly misleading, since this feature has nothing to do with implicitly-propagated exceptions, and many of the languages listed are using |
I approve. In response to @Ixrec's concern:
I would say two things: First, I don't consider this feature to only make sense if we have Second, even if we are not sure we want something, if we think that there is a "pretty good chance" that we do, then it is worth reserving the keyword. In this case, I think the answer is definitely yes: both because there is prior art in favor and because there are a lot of folks who have spoken up in favor of |
While a bonus of
Just to clarify, this is the semantics of
As @nikomatsakis puts it, I would also say that the
Neither does
Many, but not all. Haskell and Idris use
I disagree with the notion that On the choice of keyword, I think we should pick something that is consistent with |
👍 This is my strongest feeling in this area. Some sort of |
Thanks, @Centril for this fine RFC! 🥇
Here's my comment: I think we should find a better keyword than I'd like to stress this point. Our experimental We need a keyword that clearly expresses the occurrence of a failure, and is consistent with a failed attempt, a failed try. |
I do love the appreciation! 😍
Ideas? 😄 Here are some synonyms of
How so? I understand that you might say: - "I threw the ball and my dog caught it", but the meaning of
I'm wary of making such predictions either way since it is difficult; We can give technical and non-technical arguments for why
This way of framing it seems to suggest
All in all, I think @nikomatsakis put it well on #2388:
This suggests to me that |
We do need to tell people "Rust does not have exceptions", so using |
Back before 1.0, In this case, I don't think that confusion will come up. While there are still several ways to fail, they all tend to do the same thing as I'm not really opposed to I also have a general sense of uneasiness about introducing too many But at the same time I do think |
Surely no more so than than
Here's a fairly accurate check I think: https://sourcegraph.com/search?q=repogroup:crates+case:yes++\b((let|const|type|)\s%2Bfail\s%2B%3D|(fn|impl|mod|struct|enum|union|trait)\s%2Bfail)\b+max:400 The breakage doesn't seem too extensive. @rpjohnst I agree with your reasoning on
This one seems unlikely? I think at least any confusion here is quickly fixable in Rust's learning material.
True; I agree this is a problem, but let's not let the perfect be the enemy of the good? |
Why not Why not I'd prefer |
Sure; but
You don't need to reserve Regarding "what you may throw, you may catch", while I think that makes sense in English, would
This seems true to me, I can't argue with it. :) |
text/0000-throw-expr.md
Outdated
is deemed quite low. | ||
Searching for `raise` with sourcegraph indicates a very | ||
small number of uses as an identifier, but searching for `throw` gives a timeout | ||
indicating that there are no uses. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've got at least 4 results from https://sourcegraph.com/search?q=repogroup:crates+%5Cbfn%5Cs%2Bthrow%5Cb.
All of them are JS bindings (e.g. servo/servo and neon-bindings/neon), but I guess it is because of sourcegraph's selection of repositories.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Strange. I tried the following query before: https://sourcegraph.com/search?q=repogroup:crates+case:yes++\b((let|const|type|)\s%2Bthrow\s%2B%3D|(fn|impl|mod|struct|enum|union|trait)\s%2Bthrow)\b+max:400 and it timed out, but now it shows the results you reference.
Anyways, the breakage slightly is less extensive than for raise
and fail
; but all keywords in this space break minimally.
I'll update the RFC with this new info :)
I've now added some of the recent discussion to the RFC's text. |
the perfect be the enemy of the good. | ||
|
||
A strategy for introducing early-return on success with Ok-wrapping **could** | ||
be to introduce `try fn` and / or to let `return` perform an early return |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Or, go the other way around- let people break
out of try
blocks. On its own, this is probably a bad idea, as try
blocks are not loops, but it's possible 'label: try { .. break 'label value; .. }
would be sufficient. For that matter, maybe any return
or break
that leaves the try
block should be Ok
-wrapped? For example:
fn f() -> Result<T, U> {
try {
let t: T = get_a_t()?;
return t;
}
}
Maybe that's even worse.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My main concern wrt. re-purposing break
is this:
try {
// ..
loop {
// Are we breaking the loop or the try?
// Perhaps it is obvious to say "the loop", but is it really?
break 3;
}
// ..
}
Maybe that's even worse.
Probably, yeah.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding break with label:
'label: try {
..
break 'label value;
..
}
That seems workable, but it is not particularly ergonomic.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While I'm a fan of having label-break-value, I see it mostly as a way for macros to provide customized flow control constructs. I'd rather it typically be internal to libraries more than something that shows up in syntax that would be used broadly.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another, possibly bad, idea to facilitate for macros could be to introduce the special label 'try
which always refers to the closest try { .. }
block.
That way, you could just write:
try {
ok!(value);
}
where ok!(value)
expands to break 'try Try::from_ok(value)
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Centril I think my rigorous reason is I don't like introducing new names "out of thin air." Every other label has its name chosen by the source code- I would be much less bothered by some kind of 'my_label: fn f() { ... }
or 'my_label: try { ... }
than I am by introducing 'fn
or 'try
as "special" names.
A similar-but-different situation came up in #2115, where people felt wary about allowing the source code to introduce names without some primary declaration point. But in that case, the source code is still the thing determining the name- you write the same name in two places and they mean the same thing. Labels inherently have a "declaration" point, but the same reasoning applies- you write the name down in both places, as a label.
(@squishy-clouds 'static
is not a label at all- it's a lifetime, and further kind of a "monoidal identity element" like 0
or 1
or []
, so it gets a bit of a pass here.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@rpjohnst I don't think they would be so "out of thin air";
If the system of having special labels is applied out consistently per control flow block-form you could have:
'fn
'loop
'while
'for
- maybe just merge these as one
loop
?
'if
'match
'try
'async
'const
(maybe not?)
The goal of these are not to be used directly in people's code, but rather hidden away in macros so that you can invent new and interesting DSLs. The problem with having to explicitly declare the labels is that you have to pass them into the macros.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any actual use cases for a generalized "magic labels" feature like this, even in DSLs? I've never heard anyone suggest any of these except as an implementation detail of a throw/fail macro.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One use-case might be that if we had a 'try
built-in label that can be targeted by ?
, I think all of the other exception like syntax can be implemented by the eco-system (or even std
if deemed worthy) as macros. That would allow for:
- People to use break-with-value and
?
with these blocks without auto-wrapping or any other additional functionality, basically keeping it as close to current control flow as possible. - Experimentation with more exception like syntax on crates.io. Before things would settle that would also provide an explicit opt-in to specific semantics. Once things have settled, the functionality can simply be added to
std
. Since they would still be macros, there's still an opt-in to additional semantics like auto-converting a final result. Adjusting pieces ofstd
also seems easier and more future proof than changing the language itself.
I'm wondering if it would make sense to put together a proposal for this path for the new exception like syntax extensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have any actual use cases for a generalized "magic labels" feature like this, even in DSLs?
Not very concretely at least; Just hypothesizing ;)
Stemming from #2426 (review)... Would this be (eventually) allowed? let result = 'a: try {
try {
throw 'a e;
}
}; |
I suppose it could. It is consistent with
Another possibility is discussed here: https://github.com/Centril/rfcs/blob/rfc/throw-expr/text/0000-throw-expr.md#paper-exceptional-syntax |
I'm not objecting to the idea of having an expression that serves this function (though personally I think |
@joshtriplett How come? And are you saying that we should choose I do think that RFC #243 using exceptional terminology should be seen as precedent and therefore the case against exceptional terminology is less convincing. However; my view is that either @petrochenkov, @wesleywiser, @Flaise, @kjeremy, @gilescope @phaylon @alexander-irbis @0x7CFE: |
I agree with the concerns already stated, and my concerns from the I'd summarize it as: I'm generally against using exception syntax in a non-exception language, because, mainly:
These are largely philosophical/foundational, so I'm not sure there is much to address. I believe I will remain firmly in the -1 group. |
@phaylon Would you be more comfortable with
It is not included in the current proposal (and doesn't have to be).
Writing |
@ssokolow Thanks for giving me the benefit of the doubt, but it was a typo. I need to issue an RFWIMNWIS, Request for What I Meant, Not What I Said! :D |
This is a very well-writren and thoroughly researched RFC. @Centril I like how neutral you remain when discussing the options, and although you argue for Just wanted to add a couple thoughts:
|
Since the only urgent question of this RFC is the keyword reservation of |
@rfcbot fcp postpone "The Rust team is laser focused on the 2018 Edition right now", so with #2441 covering the urgent-for-the-edition part, I propose we set aside any further discussion until after the current roadmap. |
Team member @scottmcm has proposed to postpone this. The next step is review by the rest of the tagged teams: No concerns currently listed. Once a majority of reviewers approve (and none object), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up! See this document for info about what commands tagged team members can give me. |
🔔 This is now entering its final comment period, as per the review above. 🔔 |
Moderation note: @m-cat such comments are very clearly against our code of conduct. Further such behavior will result in a ban from the project. Feel free to contact us at rust-mods@rust-lang.org if you have questions. |
This comment has been minimized.
This comment has been minimized.
[Comments/questions about moderation should go to the moderation team, please.] |
This comment has been minimized.
This comment has been minimized.
This is not the place to discuss this. |
This comment has been minimized.
This comment has been minimized.
Moderation note: There are multiple discussions happening in the forum about the RFC process. Please keep process discussion in the forums and out of individual RFC PRs. |
I'm going to go ahead and close out this RFC (which is in FCP for postponement), as I think it's quite clear we won't be pushing on this in the near future, and the thread is continuing to veer well off topic. |
🖼️ Rendered
📝 Summary
Introduce diverging
throw expr
expressions, typed at!
, which eitherbreak
to the closesttry { .. }
if there is one, or if not,return
from thefn
or closure. The expression formthrow expr
is supported on edition 2018 onwards. This also means thatthrow
is reserved as a keyword.A minimal example:
💖 Thanks
To @scottmcm for reviewing.