-
Notifications
You must be signed in to change notification settings - Fork 12.9k
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
Tracking issue for ops::Try
(try_trait
feature)
#42327
Comments
A couple of pieces of bikeshedding:
(updated version of playground link from the RFC) |
|
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
Following the match expr { // Removed `Try::into_result()` here.
Ok(v) => v,
Err(e) => return Try::from_error(From::from(e)),
} The motivating examples I considered are FutureWe can implement impl<T, E> FromResult for FutureResult {
type Ok = T;
type Error = E;
fn from_error(v: Self::Error) -> Self {
future::err(v)
}
fn from_ok(v: Self::Ok) -> Self {
future::ok(v)
}
} Assuming a reasonable implementation for fn async_stuff() -> impl Future<V, E> {
let t = fetch_t();
t.and_then(|t_val| {
let u: Result<U, E> = calc(t_val);
async_2(u?)
})
}
fn fetch_t() -> impl Future<T, E> {}
fn calc(t: T) -> Result<U,E> {} That is exactly what I was trying to implement earlier today and Option
Hope that helps! |
I'm maybe very late to all this, but it occured to me this weekend that fn fun() -> SearchResult<Socks> {
search_drawer()?;
search_wardrobe()
} But in this case, the naming of the It would widen the meaning of the |
In prototyping a From there I also wished for a |
The |
Since PR #42275 has been merged, does that mean this issue has been resolved? |
@skade Note that a type can define whichever as the short-circuiting one, like @cuviper The version I ended up needing was either the @ErichDonGubler This is a tracking issue, so isn't resolved until the corresponding code is in stable. |
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 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: enum StrandFail<T> {
Success(T),
NoSolution,
QuantumExceeded,
Cycle(Strand, Minimums),
} I wanted enum StrandFail {
NoSolution,
QuantumExceeded,
Cycle(Strand, Minimums),
} But once I've got this type, then I might as well make type StrandResult<T> = Result<T, StrandFail>; 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 (On the other hand, the current definition would help readers to know what the behavior of I guess this outcome is not that surprising: the current I'd also note that, in the case of |
Is the associated |
I don't follow either. I have successfully implemented Try for a type that had an internal error representation and it felt natural having In fact is seems like there is a fairly simple pattern for making this work. impl std::ops::Try for Value {
type Ok = Self;
type Error = Self;
fn from_ok(v: Self::Ok) -> Self { v }
fn from_error(v: Self::Error) -> Self { v }
fn into_result(self) -> Result<Self::Ok, Self::Error> {
if self.is_ok() { Ok(val) } else { Err(val) }
}
} If you want to unwrap the success something like this should work: impl std::ops::Try for StrandFail<T> {
type Ok = T;
type Error = Self;
fn from_ok(v: Self::Ok) -> Self { StrandFail::Success(v) }
fn from_error(v: Self::Error) -> Self { v }
fn into_result(self) -> Result<Self::Ok, Self::Error> {
match self {
StrandFail::Success(v) => Ok(v),
other => Err(other),
}
}
} |
Defining a structural type is possible but feels pretty annoying. I agree I could use |
Great experience report, @nikomatsakis! I've also been dissatisfied with TL/DR: I think Longer: I keep using
So if nothing else, I think the description I wrote for The other thing I've been thinking about is that we should consider some slightly-crazy impls for fn cmp(&self, other: &self) -> Ordering try {
self.a.cmp(&other.a)?;
self.b.cmp(&other.b)?;
} I don't yet know whether that's the good kind of crazy or the bad kind 😆 It's certainly got some elegance to it, though, since once things are different you know they're different. And trying to assign "success" or "error" to either side of that doesn't seem to fit well. I do also note that that's another instance where the "error-conversion" isn't helpful. I also never used it in the "I want ? but it's not an error" examples above. And it's certainly known to cause inference sadness, so I wonder if it's a thing that should get limited to just Result (or potentially removed in favour of Edit: Oh, and all that makes me wonder if perhaps the answer to "I keep using Result for my errors instead of implementing Edit 2: This is not unlike #42327 (comment) above Edit 3: Seems like a |
My two cents: I like @fluffysquirrels's suggestion of splitting the trait into two traits. One for converting to a result, and another to convert from a result. But I do think we should keep the I also like @scottmcm's idea of using names that suggest break/continue rather than error/ok. |
To put the specific code in here, I like how this reads: rust/src/libcore/iter/iterator.rs Lines 1738 to 1746 in ab8b961
It's of course not quite as nice as a straight loop, but with "try methods" it would be close: self.try_for_each(move |x| try {
if predicate(&x) { return LoopState::Break(x) }
}).break_value() For comparison, I find the "error vocabulary" version really misleading: self.try_for_each(move |x| {
if predicate(&x) { Err(x) }
else { Ok(()) }
}).err() |
Can we implement Display for NoneError? It would allow the failure crate to automatically derive |
@cowang4 I'd like to try to avoid enabling any more mixing of Result-and-Option right now, as the type is unstable mostly to keep our options open there. I wouldn't be surprised if we ended up changing the design of |
@scottmcm Okay. I see your point. I'd eventually like a clean way to sugar the pattern of returning Err when an library function returns None. Maybe you know of one other than fn work_with_optional_types(pb: &PathBuf) -> Result<MyStruct, Error> {
if let Some(filestem) = pb.file_stem() {
if let Some(filestr) = filestem.to_str() {
return Ok(MyStruct {
filename: filestr.to_string()
});
}
}
Err(_)
} Once I found this experimental feature and the use failure::Error;
fn work_with_optional_types(pb: &PathBuf) -> Result<MyStruct, Error> {
Ok({
title: pb.file_stem?.to_str()?.to_string()
})
} Which almost works, except for the lack of a if option.is_none() {
return Err(_);
} |
@cowang4 I believe that would work if you implemented However, it is probably better practice to use |
@tmccombs I tried creating my own error type, but I must've done it wrong. It was like this: #[macro_use] extern crate failure_derive;
#[derive(Fail, Debug)]
#[fail(display = "An error occurred.")]
struct SiteError;
impl From<std::option::NoneError> for SiteError {
fn from(_err: std::option::NoneError) -> Self {
SiteError
}
}
fn build_piece(cur_dir: &PathBuf, piece: &PathBuf) -> Result<Piece, SiteError> {
let title: String = piece
.file_stem()?
.to_str()?
.to_string();
Ok(Piece {
title: title,
url: piece
.strip_prefix(cur_dir)?
.to_str()
.ok_or(err_msg("tostr"))?
.to_string(),
})
} And then I tried using my error type... error[E0277]: the trait bound `SiteError: std::convert::From<std::path::StripPrefixError>` is not satisfied
--> src/main.rs:195:14
|
195 | url: piece
| ______________^
196 | | .strip_prefix(cur_dir)?
| |___________________________________^ the trait `std::convert::From<std::path::StripPrefixError>` is not implemented for `SiteError`
|
= help: the following implementations were found:
<SiteError as std::convert::From<std::option::NoneError>>
= note: required by `std::convert::From::from`
error[E0277]: the trait bound `SiteError: std::convert::From<failure::Error>` is not satisfied
--> src/main.rs:195:14
|
195 | url: piece
| ______________^
196 | | .strip_prefix(cur_dir)?
197 | | .to_str()
198 | | .ok_or(err_msg("tostr"))?
| |_____________________________________^ the trait `std::convert::From<failure::Error>` is not implemented for `SiteError`
|
= help: the following implementations were found:
<SiteError as std::convert::From<std::option::NoneError>>
= note: required by `std::convert::From::from` Okay, I just realized that it wants to know how to convert from other error types to my error, which I can write generically: impl<E: failure::Fail> From<E> for SiteError {
fn from(_err: E) -> Self {
SiteError
}
} Nope... error[E0119]: conflicting implementations of trait `std::convert::From<SiteError>` for type `SiteError`:
--> src/main.rs:183:1
|
183 | impl<E: failure::Fail> From<E> for SiteError {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<T> std::convert::From<T> for T; Okay, what about impl<E: std::error::Error> From<E> for SiteError {
fn from(_err: E) -> Self {
SiteError
}
} That doesn't work either. Partly because it conflicts with my error[E0119]: conflicting implementations of trait `std::convert::From<std::option::NoneError>` for type `SiteError`:
--> src/main.rs:181:1
|
175 | impl From<std::option::NoneError> for SiteError {
| ----------------------------------------------- first implementation here
...
181 | impl<E: std::error::Error> From<E> for SiteError {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `SiteError`
|
= note: upstream crates may add new impl of trait `std::error::Error` for type `std::option::NoneError` in future versions Which is weird because I thought that error[E0277]: the trait bound `std::option::NoneError: std::error::Error` is not satisfied
--> src/main.rs:189:25
|
189 | let title: String = piece
| _________________________^
190 | | .file_stem()?
| |_____________________^ the trait `std::error::Error` is not implemented for `std::option::NoneError`
|
= note: required because of the requirements on the impl of `std::convert::From<std::option::NoneError>` for `SiteError`
= note: required by `std::convert::From::from` Do I have to write all the Maybe I should stick with |
I don't think the failure crate does that. But I could be wrong. |
Ok, so I re-examined the failure crate, and if I'm reading the documentation and different versions right, it's designed to always use the impl<E: StdError + Send + Sync + 'static> Fail for E {} https://github.com/rust-lang-nursery/failure/blob/master/failure-1.X/src/lib.rs#L218 And then a impl<F: Fail> From<F> for ErrorImpl But, it's just that since the error relevant to this rust issue, Thank you everyone for helping. I'm slowly learning Rust! 😄 |
This would make |
@calebsander thanks for kindly reply. fn check_key(v: Vec<A>, key: usize) -> bool {
let x = v.get_mut(key)?; // Option
x.not_valid().not()?; // bool
for i in x.iter() {
if i == 1 { return true }
if i == 2 { return false }
debug!("get {}" i);
}
let y = x.transform()?; // Result
y == 1
} For some condition that |
Well, unless I misunderstand your proposal, just implementing Leaving issues of converting other fn using_try() -> bool {
some_bool()?;
some_bool()?;
some_bool()
} and fn using_and() -> bool {
some_bool() &&
some_bool() &&
some_bool()
} (Admittedly, control flows like
It's not clear to me that |
No, I don't want to
I think it somehow natural when we have |
Forgive me if I missed it - why doesnt |
Not an expert here, but I can see significant problems with trying to come up with a useful error message for "something was EDIT: |
I agree with @ErichDonGubler. It's super annoying to be unable to use The only time I'd allow |
Strongly feel that we should just leave this to Also I suspect |
My usecase is indeed rough prototyping where I don't care about the errors too much. Doesn't the whole Because of the type lacking this impl I resorted to just slapping a |
…erences, r=shepmaster Use `try{}` in `try_fold` to decouple iterators in the library from `Try` details I'd like to experiment with changing the `?`/`try` desugaring and correspondingly the `Try` trait (see rust-lang#42327 for discussions about the suboptimalities of the current one) and this change would keep from needing any `cfg(bootstrap)` in iterator things. This will be lowered to the same thing, so shouldn't cause any perf issues: https://github.com/rust-lang/rust/blob/08e2d4616613716362b4b49980ff303f2b9ae654/compiler/rustc_ast_lowering/src/expr.rs#L428-L429 But ~~I'll trigger~~ I've triggered [a perf run](https://perf.rust-lang.org/compare.html?start=d65c08e9cc164b7b44de53503fae859a4fafd976&end=2c067c5235e779cd75e9f0cdfe572c64f1a12b9b) just in case. ~~EDIT: changed to a draft because of the rustfmt-only syntax error. zulip thread about it: https://rust-lang.zulipchat.com/#narrow/stream/122651-general/topic/New.20bootstrap.20rustfmt.20doesn't.20support.20syntax.20from.20sept.3F/near/213098097~~ EDIT: This now includes a rustfmt version bump to get through tidy.
Being unable to cast a |
I don't think this is being debated, so much as it locking us into the current (clearly suboptimal) design in some ways. |
For anyone following along here: I've posted a new RFC with an alternative |
@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. |
Definitely 👍 |
Now that the Try v2 RFC has been accepted, shall we close this in favor of #84277 ? |
@bstrie There's still |
|
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
Steps / History
Stabilizing this will allow people to implementIterator::try_fold
As part of stabilizing, re-open PR Document implementingtry_fold
for iterators for internal iteration #62606 to document implementing try_fold for iteratorsEnsure 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 havefold
be implemented in terms oftry_fold
, so that both don't need to be overridden.)try_trait_v2
tracking issueLower
?
toTry
instead ofCarrier
#42275Deprecate and remove these in favour of the new versions.
Unresolved Questions
These resulted in a new rust-lang/rfcs#3058, tracked in #84277
Try
traitTryContinue
API: Tracking issue forops::Try
(try_trait
feature) #42327 (comment) and Tracking issue forops::Try
(try_trait
feature) #42327 (comment)ops::Try
(try_trait
feature) #42327 (comment)ops::Try
(try_trait
feature) #42327 (comment)ops::Try
(try_trait
feature) #42327 (comment)The text was updated successfully, but these errors were encountered: