extend ?
to operate over other types by nikomatsakis · Pull Request #1859 · rust-lang/rfcs (original) (raw)
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 andprivacy statement. We’ll occasionally send you account related emails.
Already on GitHub?Sign in to your account
Conversation148 Commits10 Checks0 Files changed
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.Learn more about bidirectional Unicode characters
[ Show hidden characters]({{ revealButtonHref }})
shepmaster, Connicpu, clarfonthey, mglagla, burdges, soltanmm, lpil, delacian, tikue, Cldfire, and 49 more reacted with thumbs up emoji shepmaster, est31, eddyb, SimonSapin, carols10cents, jamsch0, clarfonthey, killercup, aturon, lpil, and 33 more reacted with heart emoji
nikomatsakis added the T-lang
Relevant to the language team, which will review and decide on the RFC.
label
I don't know how to make a "rendered" link anymore. =)
@@ -0,0 +1,478 @@ |
---|
- Feature Name: (fill me in with a unique ident, my_awesome_feature) |
- Start Date: (fill me in with today's date, YYYY-MM-DD) |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Did you want to fill these in? ^^
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done.
### What to name the trait |
---|
A number of names have been proposed for this trait. The original name |
was `Carrier`, as the trait "carrier" an error value. A proposed |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unclear if
as the trait "carrier" an error value
Is a typo or an example of why the previous name was poor. I'd have expected "carries"
.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That is a typo.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fixed.
Thank you @nikomatsakis! I am excited to watch the progress of this RFC!
This more general behavior can be taught at the same time as the
?
operator
Depending on the level of detail envisioned, I'd argue that doing this may be too much. Explicit return types already seem different enough from the exception-like handling that many newcomers are used to; expanding the universe immediately by making it generic might be overwhelming.
I'd lean towards a throwaway sentence that indicates that the behavior of ?
can be overloaded, similar to other operators, with a link to the API docs. That is a reasonable location to have detailed implementation instructions.
The
Option
type includes an impl as follows
I don't have a strong opinion on implementing Try
for Option
, but I'd hate to see the RFC get bogged down on that point. I'd much rather have the trait at all so I can implement it for my own types. If the tide turns against impl Try for Option
, I'd hope that the trait itself perseveres.
TL;DR: I'm OK with Result
as used in this RFC
The Try trait uses Result as its return type.
In the lead-up to the RFC, I expressed mild concern at the use of Result
here. I do weakly agree with the RFC: the use of Result
is at least in the correct semantic ballpark.
My main concern is against using Result
as a generic "a or b" container type solely because it's what we already have in the standard library. I don't think that concern is an issue here, but it's near the line.
A very common problem that new rustaceans hit is trying to use ?
in main
or some other method that doesn't return Result
. The current error message that you get says the trait bound `(): std::ops::Carrier` is not satisfied
which is not particularly helpful. If this RFC gets accepted and implemented, will the error message remain as it is and just say "Try" instead of "Carrier"?
I totally understand if this RFC is orthogonal to the issue I'm interested in :)
EDIT: Aha, found the discussion more focused on ? in main, was looking at RFCs and issues and couldn't find it a minute ago. Seems like that will be a totally separate RFC than this one, correct?
I personally think that TryResult
would be better as:
enum TryResult<T, R> { Yield(T), Return(R), }
Than Abrupt
and Ok
, because this seems a bit more natural. It also allows the variants to be exported in prelude whereas Ok
would conflict with Result::Ok
.
If this RFC gets accepted and implemented, will the error message remain as it is and just say "Try" instead of "Carrier"?
I agree we need to correct the error message. I also think that it's worth thinking and drafting error messages here, in this RFC -- too often we leave it as an afterthought. Do you have suggestions for the wording? That said, when you wrote "Seems like that will be a totally separate RFC than this one, correct?", I'd say that this is the overkill. That is, we can (and do!) improve error messages without the need for RFCs.
I would think the message would be something like:
cannot use the
?
operator in a function that returns()
We could also go further and analyze the type to which ?
is applied and figure out the set of legal return types for main
. So if you were doing foo.write()?
(i.e., applying ?
to an io::Result
), then we could offer a suggestion like "consider changing the return type to Result<(), io::Error>
" or perhaps just "consider changing the return type to a Result
"). If we really wanted to go crazy, though, we'd maybe some further annotations to suggest something like io::Result<()>
, but that would require some form of annotation on io::Error
that links to the io::Result
alias, I imagine. It seems like starting with suggesting that the function return a Result
would be so much better than we have.
I can add some text to this effect.
UPDATE: Done.
@shepmaster @clarcharr Regarding the use of Result
: I have explained my reasons in the RFC. I don't feel extremely strongly about this and I'm certainly open to adding a new type. Result
I think is what I'd prefer because (as I wrote) it seems reasonably appropriate and I don't feel a strong need for a throw-away enum here. I guess I'd like the feedback from @rust-lang/libs in general about this point.
I don't have a strong opinion on implementing Try for Option, but I'd hate to see the RFC get bogged down on that point. I'd much rather have the trait at all so I can implement it for my own types. If the tide turns against impl Try for Option, I'd hope that the trait itself perseveres.
Indeed, I would remove Option
if forced, but I have heard from many people who strongly want support for Option
-- myself included =) -- and hence I am inclined to keep it in. The main objection I have heard is that interconversion between Option
and Result
is semantically loose, and this RFC avoids that.
Ah, @clarcharr, regarding the names Yield
and Return
, I considered Yield
as a name, but avoided it for fear of confusion with coroutines and generators, which are also under consideration. Return
is perhaps better than Abrupt
, except that the value is not always returned, it may be propagated to the innermost enclosing catch
(if only that were implemented...).
@nikomatsakis Personally I think that throwway types are one of the things that makes Rust really powerful. In projects I tend to use enums extensively even if they only have two variants, because I think it makes things more clear, and they're not as equivalent to bool
and usize
like in C. I don't think that the argument for using methods on Result
is very compelling imho because I can't see many implementations using them. Could easily be proven wrong, though.
As far as Yield goes, maybe Continue?
I agree we need to correct the error message. I also think that it's worth thinking and drafting error messages here, in this RFC -- too often we leave it as an afterthought. Do you have suggestions for the wording? That said, when you wrote "Seems like that will be a totally separate RFC than this one, correct?", I'd say that this is the overkill. That is, we can (and do!) improve error messages without the need for RFCs.
Oh, I guess in my head improving the error message seems tied up with this discussion about treating ? differently in main, I'm glad you're in favor of thinking about the error message here though!
I would think the message would be something like:
cannot use the ? operator in a function that returns ()
That would be better than what's currently there, definitely. I'd also like a companion note that makes a recommendation like "Consider extracting the code where you want to use the ?
into a function that returns Result
and using a match
statement here to handle the return value from the new function". I realize that's long.
We could also go further and analyze the type to which ? is applied and figure out the set of legal return types for main. So if you were doing foo.write()? (i.e., applying ? to an io::Result), then we could offer a suggestion like "consider changing the return type to Result<(), io::Error>" or perhaps just "consider changing the return type to a Result"). If we really wanted to go crazy, though, we'd maybe some further annotations to suggest something like io::Result<()>, but that would require some form of annotation on io::Error that links to the io::Result alias, I imagine. It seems like starting with suggesting that the function return a Result would be so much better than we have.
So I don't think "consider changing the return type to a Result" is an appropriate suggestion for main
, since the return type of main
MUST be ()
, and we'll have just suggested they make a change that leads to another error. Similarly with methods of a trait that someone might be implementing. If we can't distinguish those two situations from cases when the person totally controls the function signature, then I don't think we should have this suggestion at all :-/
I do like what you have in the text for the case when someone tries to use ?
on something that doesn't implement it ("?
cannot be applied to a value of type Foo
"). Should we list types that DO implement Try
in scope? Or should we suggest implementing Try
on Foo
? That might not always be a good suggestion...
So I don't think "consider changing the return type to a Result" is an appropriate suggestion for main, since the return type of main MUST be ()
I agree, although my preference would be to implement the solutions proposed in this thread on internals, such that you could change the return type on main
If we can't distinguish those two situations from cases when the person totally controls the function signature, then I don't think we should have this suggestion at all :-/
In any case, good point, I see no reason we cannot distinguish the context. In the case of a trait, one could also imagine that, in the case that the trait is thread-local, suggesting that one might consider modifying the trait itself.
Should we list types that DO implement Try in scope? Or should we suggest implementing Try on Foo? That might not always be a good suggestion...
To me it seems like overkill. In the first case, it doesn't seem worth mentioning types that do implement try, since the value in question is not of those types. In the second, it's not clear that implementing try is a good idea (or even possible, given the coherence rules).
Consider extracting the code where you want to use the ? into a function that returns Result and using a match statement here to handle the return value from the new function
Ah, I overlooked this. I don't quite understand what pattern you are suggesting, can you maybe given an example?
When catch
blocks are implemented, the error message could advise to introduce one. This would also work for main()
.
Consider extracting the code where you want to use the ? into a function that returns Result and using a match statement here to handle the return value from the new function
Ah, I overlooked this. I don't quite understand what pattern you are suggesting, can you maybe given an example?
Oh, I just mean like in this comment, where your main
function is just:
fn main() {
match do_all_the_things() {
Ok(_) => //whatever
Err(e) => // handle any errors nicely
}
}
and then do_all_the_things
and all the functions it calls can return Result
and use ?
.
When catch blocks are implemented, the error message could advise to introduce one. This would also work for main().
Agreed.
This comment is actually a better example.
I see. It seems to me that this is a lot to fit into the immediate error message, but it might be a good candidate for the extended error message.
<3 love the latest changes in f89568b, this looks great now :)
bluss mentioned this pull request
12 tasks
Thx @bluss for pointing me here. I asked in rust-lang/rust#31436 why the conversion was implemented using From
instead of Into
. As it turns out changing this now would be rather difficult (breaking existing code).
I thought I needed Into
to have a generic error type in a possibly external crate, and make local errors convertible to this generic error. I found a solution (which is probably obvious to most experts in here): have a custom IntoCustomError
trait, and implement From
based on that. Maybe this could be used as an example in the book or somewhere else: https://play.rust-lang.org/?gist=41a425fc633ddd81b90a92e3479c9886
aturon removed the final-comment-period
Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised.
label
🔔 This is now entering its final comment period, as per the review above. 🔔
rfcbot added the final-comment-period
Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised.
label
The final comment period is now complete.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(typo)
```rust |
fn foo() -> Poll<T, E> { |
let x = bar()?; // propogate error case |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"propagate"
```rust |
fn foo() -> Result<T, E> { |
let x = bar()?; // propogate error case |
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"propagate"
frewsxcv added a commit to frewsxcv/rust that referenced this pull request
frewsxcv added a commit to frewsxcv/rust that referenced this pull request
ghost mentioned this pull request
Reviewers
shepmaster shepmaster left review comments
RalfJung RalfJung left review comments
bluss bluss left review comments
soltanmm soltanmm left review comments
repax repax requested changes
Labels
Proposals relating to error handling.
Standard library implementations related proposals.
Will be merged/postponed/closed in ~10 calendar days unless new substational objections are raised.
Relevant to the language team, which will review and decide on the RFC.