Repository navigation
Tracking issue for ops::Try (try_trait feature) #42327
Copy link
Copy link
Closed
Labels
A-error-handlingArea: Error handlingArea: Error handlingB-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.Blocker: Approved by a merged RFC and implemented but not stabilized.B-unstableBlocker: Implemented in the nightly compiler and unstable.Blocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCI-libs-radarLibs issues that are tracked on the team's radar.Libs issues that are tracked on the team's radar.T-langRelevant to the language teamRelevant to the language teamT-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]
Description
Feature gate: #![feature(try_trait)]
This is a tracking issue for the Try trait from rust-lang/rfcs#1859.
Split off from #31436 for clarity (per #42275 (comment))
Public API
pub mod core {
pub mod result {
impl<T, E> ops::Try for Result<T, E> {
type Ok = T;
type Error = E;
fn into_result(self) -> Self {}
fn from_ok(v: T) -> Self {}
fn from_error(v: E) -> Self {}
}
}
pub mod option {
#[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, Hash)]
pub struct NoneError;
impl<T> ops::Try for Option<T> {
type Ok = T;
type Error = NoneError;
fn into_result(self) -> Result<T, NoneError> {}
fn from_ok(v: T) -> Self {}
fn from_error(_: NoneError) -> Self {}
}
}
pub mod ops {
mod r#try {
pub trait Try {
type Ok;
type Error;
fn into_result(self) -> Result<Self::Ok, Self::Error>;
fn from_error(v: Self::Error) -> Self;
fn from_ok(v: Self::Ok) -> Self;
}
}
pub use self::r#try::Try;
}
}Steps / History
-
Stabilizing this will allow people to implementIterator::try_fold-
As part of stabilizing, re-open PR Document implementingtry_foldfor iterators for internal iteration #62606 to document implementing try_fold for iterators -
Ensure that the default implementations of other things have the desired long-term DAG, since changing them is essentially impossible later. (Specifically, it would be nice to havefoldbe implemented in terms oftry_fold, so that both don't need to be overridden.) - Moved these to the
try_trait_v2tracking issue
-
-
Deprecate and remove these in favour of the new versions.
Unresolved Questions
These resulted in a new rust-lang/rfcs#3058, tracked in #84277
- Open design questions around the shape of the
Trytrait-
TryContinueAPI: Tracking issue forops::Try(try_traitfeature) #42327 (comment) and Tracking issue forops::Try(try_traitfeature) #42327 (comment) - Support trace capturing: Tracking issue for
ops::Try(try_traitfeature) #42327 (comment)
-
- Usability concerns with the current trait
Reacted by Kevin Cox, Alice McKean, Erich Gubler, Aymeric Beaumet, Dror Levin, Luo Jia / Zhouqi Jiang, Guillaume Depardon, VitalyR, ratijas, shekohex and 65 more
Reacted by Luo Jia / Zhouqi Jiang, Tyler Neely, hugecheese, Ross, Raphaël Thériault, jtsoundy, tema3210, Vladislav, Dr. Maxim Orlovsky, Diva Martínez and 1 more
Reacted by Xie Ruifeng and Sayan
Metadata
Metadata
Assignees
Labels
A-error-handlingArea: Error handlingArea: Error handlingB-RFC-implementedBlocker: Approved by a merged RFC and implemented but not stabilized.Blocker: Approved by a merged RFC and implemented but not stabilized.B-unstableBlocker: Implemented in the nightly compiler and unstable.Blocker: Implemented in the nightly compiler and unstable.C-tracking-issueCategory: An issue tracking the progress of sth. like the implementation of an RFCCategory: An issue tracking the progress of sth. like the implementation of an RFCI-libs-radarLibs issues that are tracked on the team's radar.Libs issues that are tracked on the team's radar.T-langRelevant to the language teamRelevant to the language teamT-libs-api[DEPRECATED; DO NOT USE][DEPRECATED; DO NOT USE]
Activity
glaebhoerl commentedon Jun 28, 2017
A couple of pieces of bikeshedding:
Do we have a particular motivation to call the associated type
Errorinstead ofErr? Calling itErrwould make it line up withResult: the other one is already calledOk.Do we have a particular motivation for having separate
from_errorandfrom_okmethods, instead of a singlefrom_resultwhich would be more symmetric withinto_resultwhich is the other half of the trait?(updated version of playground link from the RFC)
scottmcm commentedon Jun 28, 2017
@glaebhoerl
ErrorvsErrwas discussed related toTryFromin Tracking issue for TryFrom/TryInto traits #33417 (comment) and Rename TryFrom's associated type and implement str::parse using TryFrom. #40281; I assume the name was chosen here for similar reasons.Resultthat they're trying to turn into aT:Try. I preferTry::from_okandTry::from_errorto always callingTry::from_result(Ok(andTry::from_result(Err(, and I'm happy to just impl the two methods over writing out the match. Perhaps that's because I think ofinto_resultnot asInto<Result>, but as "was it pass or fail?", with the specific type beingResultas an unimportant implementation detail. (I don't want to suggest or reopen the "there should be a new type for produce-value vs early-return, though.) And for documentation, I like thatfrom_errortalks about?(or eventuallythrow), whilefrom_oktalks about success-wrapping (catchblocks are notOk-wrapping their value #41414), rather than having them both in the same method.fluffysquirrels commentedon Jun 30, 2017
I'm not sure if this is the correct forum for this comment, please redirect me if it's not 😃. Perhaps this should have been on rust-lang/rfcs#1859 and I missed the comment period; oops!
I was wondering if there is a case for splitting up the
Trytrait or removing theinto_resultmethod; to me currentlyTryis somewhat like the sum of traitsFromResult(containingfrom_errorandfrom_ok) andIntoResult(containinginto_result).FromResultenables highly ergonomic early exit with the?operator, which I think is the killer use case for the feature. I thinkIntoResultcan already be implemented neatly with per-use-case methods or asInto<Result>; am I missing some useful examples?Following the
Trytrait RFC,expr?could desugar as:The motivating examples I considered are
FutureandOption.Future
We can implement
FromResultnaturally forstruct FutureResult: Futureas:Assuming a reasonable implementation for
?that will return from animpl Futurevalued function when applied to aResult::Errthen I can write:That is exactly what I was trying to implement earlier today and
Trynails it! But if we tried to implement the currentTryforFuturethere is perhaps no canonical choice forinto_result; it could be useful to panic, block, or poll once, but none of these seems universally useful. If there were nointo_resultonTryI can implement early exit as above, and if I need to convert aFutureto aResult(and thence to anyTry) I can convert it with a suitable method (call thewaitmethod to block, call somepoll_once() -> Result<T,E>, etc.).Option
Optionis a similar case. We implementfrom_ok,from_errnaturally as in theTrytrait RFC, and could convertOption<T>toResult<T, Missing>simply witho.ok_or(Missing)or a convenience methodok_or_missing.Hope that helps!
skade commentedon Jul 30, 2017
I'm maybe very late to all this, but it occured to me this weekend that
?has a rather natural semantic in cases where you'd like to short-circuit on success.But in this case, the naming of the
Trytraits methods don't fit.It would widen the meaning of the
?operator, though.cuviper commentedon Oct 25, 2017
In prototyping a
try_foldfor rayon, I found myself wanting something likeTry::is_error(&self)for theConsumer::full()methods. (Rayon has this because consumers are basically push-style iterators, but we still want to short-circuit errors.) I instead had to store the intermediate values asResult<T::Ok, T::Err>so I could callResult::is_err().From there I also wished for a
Try::from_resultto get back toTat the end, but amatchmapping toT::from_okandT::from_errorisn't too bad.fluffysquirrels commentedon Oct 28, 2017
The
Trytrait can provide afrom_resultmethod for ergonomics iffrom_okandfrom_errorare required. Or vice versa. From the examples given I see a case for offering both.ErichDonGubler commentedon Dec 29, 2017
Since PR #42275 has been merged, does that mean this issue has been resolved?
scottmcm commentedon Dec 29, 2017
@skade Note that a type can define whichever as the short-circuiting one, like
LoopStatedoes for someIteratorinternals. Perhaps that would be more natural ifTrytalked about "continuing or breaking" instead of success or failure.@cuviper The version I ended up needing was either the
Oktype or theError-in-original type. I'm hoping that destructure-and-rebuild is a general enough thing that it'll optimize well and a bunch of special methods on the trait won't be needed.@ErichDonGubler This is a tracking issue, so isn't resolved until the corresponding code is in stable.
nikomatsakis commentedon Feb 20, 2018
Experience report:
I've been a little frustrated trying to put this trait into practice. Several times now I've been tempted to define my own variant on
Resultfor whatever reason, but each time I've wound up just usingResultin the end, mostly because implementingTrywas too annoying. I'm not entirely sure if this is a good thing or not, though!Example:
In the new solver for the Chalk VM, I wanted to have an enum that indicates the result of solving a "strand". This had four possibilities:
I wanted
?, when applied to this enum, to unwrap "success" but propagated all other failures upward. However, in order to implement theTrytrait, I would have had to define a kind of "residual" type that encapsulates just the error cases:But once I've got this type, then I might as well make
StrandResult<T>an alias:and this is what I did.
Now, this doesn't necessarily seem worse than having a single enum -- but it does feel a bit odd. Usually when I write the documentation for a function, for example, I don't "group" the results by "ok" and "error", but rather talk about the various possibilities as "equals". For example:
Note that I didn't say "we return
Err(StrandFail::NoSolution). This is because theErrjust feels like this annoying artifact I have to add.(On the other hand, the current definition would help readers to know what the behavior of
?is without consulting theTryimpl.)I guess this outcome is not that surprising: the current
Tryimpl forces you to use?on things that are isomorphic toResult. This uniformity is no accident, but as a result, it makes it annoying to use?with things that are not basically "justResult". (For that matter, it's basically the same problem that gives rise toNoneError-- the need to artificially define a type to represent the "failure" of anOption.)I'd also note that, in the case of
StrandFail, I don't particularly want theFrom::fromconversion that ordinary results get, though it's not hurting me.glaebhoerl commentedon Feb 20, 2018
Is the associated
Try::Errortype ever exposed to / used by the user directly? Or is it just needed as part of the desugaring of?? If the latter, I don't see any real problem with just defining it "structurally" -type Error = Option<Option<(Strand, Minimums)>>or whatever. (Having to figure out the structural equivalent of the "failure half" of theenumdefinition isn't great, but seems less annoying than having to rejigger the whole public-facing API.)kevincox commentedon Feb 20, 2018
I don't follow either. I have successfully implemented Try for a type that had an internal error representation and it felt natural having
OkandErrorbeing the same type. You can see the implementation here: https://github.com/kevincox/ecl/blob/8ca7ad2bc4775c5cfc8eb9f4309b2666e5163e02/src/lib.rs#L298-L308In fact is seems like there is a fairly simple pattern for making this work.
If you want to unwrap the success something like this should work:
nikomatsakis commentedon Feb 20, 2018
Defining a structural type is possible but feels pretty annoying. I agree I could use
Self. It feels a bit wacky to me though that you can then use?to convert fromStrandResulttoResult<_, StrandResult>etc.89 remaining items
thomcc commentedon Oct 3, 2020
Strongly feel that we should just leave this to
unwrap. If we added a todo_err it should return an actual error type, which begs the question of what error type to return.Also I suspect
fn todo_err(self) -> Result<Self, !>would have the obvious problem of requiring!to stabilize, which uh, well, Someday.PvdBerg1998 commentedon Oct 4, 2020
My usecase is indeed rough prototyping where I don't care about the errors too much. Doesn't the whole
NoneErrorsuffer from these problems you listed? If it is decided that it should exist (which I think is a good thing, at least for prototyping), I believe it should implErroras it is named to be one.Because of the type lacking this impl I resorted to just slapping a
.ok_or("error msg")on it, which works also but is less convenient.Rollup merge of rust-lang#77877 - scottmcm:fewer-try-trait-method-ref…
smmalis37 commentedon Dec 1, 2020
Being unable to cast a
NoneErrorto adyn std::error:Errordefinitely feels like an ergonomics roadbump to me. I get that there's no great message that could be put in it, but if I'm just throwing everything into a trait object I probably don't care too much about the details of each error to begin with. It'd be really nice for prototype code to be able to returnResult<Foo, Box<dyn Error>>everywhere and have?work withOption. Given that I am explicitly declaring that I don't care much about the errors right now, having to call.ok_oror some other adapter every time would really get in the way of the "quick prototyping" ideal.thomcc commentedon Dec 1, 2020
I don't think this is being debated, so much as it locking us into the current (clearly suboptimal) design in some ways.
scottmcm commentedon Jan 10, 2021
For anyone following along here: I've posted a new RFC with an alternative
Try/?design at rust-lang/rfcs#3058scottmcm commentedon Apr 17, 2021
@yaahc I was going through the checkboxes here, and wanted to check whether you'd be fine saying that the one referencing your comment #42327 (comment) is resolved by the new RFC.
yaahc commentedon Apr 19, 2021
Definitely 👍
bstrie commentedon Apr 20, 2021
Now that the Try v2 RFC has been accepted, shall we close this in favor of #84277 ?
scottmcm commentedon Apr 20, 2021
@bstrie There's still
#[unstable]attributed in-tree referencing this, so I think this should stay open until those items have all been deleted. I added a checkbox to the OP for that.scottmcm commentedon Sep 1, 2021
Tryv1 has been completely removed from master (https://github.com/rust-lang/rust/search?q=42327&type=code).