-
Notifications
You must be signed in to change notification settings - Fork 13k
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 try_trait_v2
, A new design for the ?
desugaring (RFC#3058)
#84277
Comments
Implement the new desugaring from `try_trait_v2` ~~Currently blocked on rust-lang#84782, which has a PR in rust-lang#84811 Rebased atop that fix. `try_trait_v2` tracking issue: rust-lang#84277 Unfortunately this is already touching a ton of things, so if you have suggestions for good ways to split it up, I'd be happy to hear them. (The combination between the use in the library, the compiler changes, the corresponding diagnostic differences, even MIR tests mean that I don't really have a great plan for it other than trying to have decently-readable commits. r? `@ghost` ~~(This probably shouldn't go in during the last week before the fork anyway.)~~ Fork happened.
Implement the new desugaring from `try_trait_v2` ~~Currently blocked on rust-lang/rust#84782, which has a PR in rust-lang/rust#84811 Rebased atop that fix. `try_trait_v2` tracking issue: rust-lang/rust#84277 Unfortunately this is already touching a ton of things, so if you have suggestions for good ways to split it up, I'd be happy to hear them. (The combination between the use in the library, the compiler changes, the corresponding diagnostic differences, even MIR tests mean that I don't really have a great plan for it other than trying to have decently-readable commits. r? `@ghost` ~~(This probably shouldn't go in during the last week before the fork anyway.)~~ Fork happened.
We have a problem in our project related to the new question mark desugaring. We use the After updating to the latest nightly toolchain this stack trace collection started to work differently. I've created a small project for the demo: https://github.com/artemii235/questionmark_track_caller_try_trait_v2
Is there a way to make the track caller work the same way as it was before? Maybe we can use some workaround in our code? Thanks in advance for any help! |
That's interesting -- maybe |
From the description:
@artemii235 Do you mind opening a separate issue? |
No objections at all 🙂 I've just created it #87401. |
May i suggest changing |
How do I use |
Use |
Why the implementation of
Clarification is welcome as an error type implementing only |
see #31436 (comment) |
Hi, I'm keen to see this stabilized. Is there any work that can be contributed to push this forward? It would be my first Rust contribution, but I have a little experience working on compiler code (little bit of LLVM and KLEE in college). |
@BGR360 Unfortunately the main blockers here are unknowns, not concrete-work-needing-to-be-done, so it's difficult to push forward. It's hard to ever confirm for sure that people don't need the trait split into parts, for example. Have you perhaps been trying it out on nightly? It's be great to get experience reports -- good or bad -- about how things went. (For example, #42327 (comment) was a big help in moving to this design from the previous one.) If it was good, how did you use it? If it was bad, what went wrong? In either case, was there anything it kept you from doing which you would have liked to, even if you didn't need it? |
Experience Report@scottmcm I have tried Overall my experience with this feature is positive. It may end up being critical for my professional work in Rust. My use case is very similar to @artemii235: #84277 (comment). At my work, we need a way to capture the sequence of code locations that an error propagates through after it is created. We aren't able to simply use We would love to be able to do this in our Rust code using just the To get this to work, I made express use of the fact that you can implement multiple different I'd be happy to give more specifics on how I achieved my use case either here or on Zulip, just let me know :) Pros:
Cons:
|
Experience reportI was using Pros:
Cons:
Overall this v2 is a clear downgrade for this particular use case however the end result isn't too bad. If this is making other use cases possible it is likely worth it with better names and docs. The full change: https://gitlab.com/kevincox/ecl/-/commit/a1f348633afd2c8dd269f95820f95f008b461c9e |
This is actually a little bit unfortunate, in retrospect. It would be much better if I could just make use of
To illustrate, here's how things work in my experiment: pub struct ErrorStack<E> {
stack: ..,
inner: E,
}
impl<E> ErrorStack<E> {
/// Construst new ErrorStack with the caller location on top.
#[track_caller]
fn new(e: E) -> Self { ... }
/// Push location of caller to self.stack
#[track_caller]
fn push_caller(&mut self) { ... }
/// Return a new ErrorStack with the wrapped error converted to F
fn convert_inner<F: From<E>>(f: F) -> ErrorStack<F> { ... }
}
pub enum MyResult<T, E> {
Ok(T),
Err(ErrorStack<E>),
}
pub use MyResult::Ok;
pub use MyResult::Err;
impl<T, E> Try for MyResult<T, E> {
type Output = T;
type Residual = MyResult<Infallible, E>;
/* equivalent to std::result::Result's Try impl */
}
/// Pushes an entry to the stack when one [`MyResult`] is coerced to another using the `?` operator.
impl<T, E, F: From<E>> FromResidual<MyResult<Infallible, E>> for MyResult<T, F> {
#[inline]
#[track_caller]
fn from_residual(residual: MyResult<Infallible, E>) -> Self {
match residual {
// seems like this match arm shouldn't be needed, but idk the compiler complained
Ok(_) => unreachable!(),
Err(mut e) => {
e.push_caller();
Err(e.convert_inner())
}
}
}
}
/// Starts a new stack when a [`std::result::Result`] is coerced to a [`Result`] using `?`.
impl<T, E> FromResidual<std::result::Result<Infallible, E>> for Result<T, E> {
#[inline]
#[track_caller]
fn from_residual(residual: std::result::Result<Infallible, E>) -> Self {
match residual {
// seems like this match arm shouldn't be needed, but idk the compiler complained
std::result::Result::Ok(_) => unreachable!(),
std::result::Result::Err(e) => Err(StackError::new(e)),
}
}
} If impl<E, F: From<E>> From<ErrorStack<E>> for ErrorStack<F> {
#[track_caller]
fn from(mut e: ErrorStack<E>) -> Self {
e.push_caller();
e.convert_inner()
}
} However, this does not work because it conflicts with the blanket I could limit my fn foo() -> MyResult<(), io::Error> {
fs::File::open("foo.txt")?;
}
fn bar() -> MyResult<(), io::Error> {
// I need bar to show up in error traces, so I wrap with Ok(..?).
// Without my custom MyResult, I am unable to intercept this invocation of the `?` operator, because
// the return type is the same as that of `foo`.
Ok(foo()?)
} |
Experience report I used try_traits_v2 to implement FromResidual for Result<Infallible, impl Into> and Option for a custom Iterator<Item=Result<Value,EvalError>>. I use this in an AST walker where each node can return zero or more Values, or an error. Before I implemented FromResidual I had to use Result<iterator, EvalError> as return type in the visitor methods in order to use "?", which caused a lot of Ok-wrapping and having to handle the fact that an error could be both the outer result or inside the iterator. Also, returning an empty iterator was very explicit. After FromResidual was implemented I could change the return type on the visitors "iterator" and still use Err(EvalError)? to return errors inside an iterator, or return an empty iterator with None?. All the Ok-wrapping went away. It was quite straightforward to figure out what was needed. The only stumbling blocks was that the default for R in FromResidual made the RustRover autocomplete the impl skeleton incorrectly for my usage, which caused a few minutes of head-scratching. The other thing that took a few tries was to see that the Ok type on the Result impl must be Infallible, but in that case the type errors from rustc were quite helpful. All in all, great feature. Works for me. I don't mind the "Residual" name, even though it's not intuitive for me. Just another concept to learn. The default for R might cause more problems than it solves, though. |
I recently tried this out by implementing an I still don't understand why the type of It's possible that I got something wrong or had incorrect expectations but wanted to share my experience in case this wasn't intended. Thanks! |
@paulyoung as you've written, In impl<L, R, F: From<L>> FromResidual<Either<L, Infallible>> for Either<F, R> { With |
At risk of sounding overly ambitious, let's… ship this? The overall sentiment has been positive. People feel it's solving real problems and filling an important gap. Two quick decisions could unblock us:
Thoughts on pushing this across the finish line? |
…=scottmcm Explicitly specify type parameter on FromResidual for Option and ControlFlow. ~~Remove type parameter default `R = <Self as Try>::Residual` from `FromResidual`~~ _Specify default type parameter on `FromResidual` impls in the stdlib_ to work around rust-lang#99940 / rust-lang#87350 ~~as mentioned in rust-lang#84277 (comment). This does not completely fix the issue, but works around it for `Option` and `ControlFlow` specifically (`Result` does not have the issue since it already did not use the default parameter of `FromResidual`). ~~(Does this need an ACP or similar?)~~ ~~This probably needs at least an FCP since it changes the API described in [the RFC](rust-lang/rfcs#3058). Not sure if T-lang, T-libs-api, T-libs, or some combination (The tracking issue is tagged T-lang, T-libs-api).~~ This probably doesn't need T-lang input, since it is not changing the API of `FromResidual` from the RFC? Maybe needs T-libs-api FCP?
…=scottmcm Explicitly specify type parameter on FromResidual for Option and ControlFlow. ~~Remove type parameter default `R = <Self as Try>::Residual` from `FromResidual`~~ _Specify default type parameter on `FromResidual` impls in the stdlib_ to work around rust-lang#99940 / rust-lang#87350 ~~as mentioned in rust-lang#84277 (comment). This does not completely fix the issue, but works around it for `Option` and `ControlFlow` specifically (`Result` does not have the issue since it already did not use the default parameter of `FromResidual`). ~~(Does this need an ACP or similar?)~~ ~~This probably needs at least an FCP since it changes the API described in [the RFC](rust-lang/rfcs#3058). Not sure if T-lang, T-libs-api, T-libs, or some combination (The tracking issue is tagged T-lang, T-libs-api).~~ This probably doesn't need T-lang input, since it is not changing the API of `FromResidual` from the RFC? Maybe needs T-libs-api FCP?
Rollup merge of rust-lang#128954 - zachs18:fromresidual-no-default, r=scottmcm Explicitly specify type parameter on FromResidual for Option and ControlFlow. ~~Remove type parameter default `R = <Self as Try>::Residual` from `FromResidual`~~ _Specify default type parameter on `FromResidual` impls in the stdlib_ to work around rust-lang#99940 / rust-lang#87350 ~~as mentioned in rust-lang#84277 (comment). This does not completely fix the issue, but works around it for `Option` and `ControlFlow` specifically (`Result` does not have the issue since it already did not use the default parameter of `FromResidual`). ~~(Does this need an ACP or similar?)~~ ~~This probably needs at least an FCP since it changes the API described in [the RFC](rust-lang/rfcs#3058). Not sure if T-lang, T-libs-api, T-libs, or some combination (The tracking issue is tagged T-lang, T-libs-api).~~ This probably doesn't need T-lang input, since it is not changing the API of `FromResidual` from the RFC? Maybe needs T-libs-api FCP?
@scottmcm This was discussed a few weeks ago in a libs-api meeting in which you participated. We decided that this feature probably needs a dedicate design meeting. Could you prepare for the design meeting a document breaking down the open questions, what could be partially stabilized, etc. We would then discuss this during in the first hour of the next libs-api meeting (or the next one after you have had a chance to compile the document). |
Is there a bad interaction between FromResidual and the orphan rule? The following violates the orphan rule:
I have a function which wants to return partial output if it's forced to stop partway through. In this particular case, it's a data structure search function which wants to record the major steps of a failing search to inform a potential future insert. I want to write I'm successfully using these same traits to implement Option -> () short circuiting, so I could use a I haven't read the discussions about these traits carefully, and am too much of a beginner to have a concrete suggestion, a confident analysis, or even confidence that what I want is reasonable and the answer isn't "better try block ergonomics". But it seems suspicious to me that I'm hitting orphan rule issues given that I'm using a local type to define an Option -> E conversion. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
Should the Try trait also provide |
I think that this thought does show some merit. Currently, this can be achieved using the return value from Implementing this does not, obviously, cover secondary variants, such as those covered by e.g. |
imo if you have |
|
Ok so I understand implementing Try for Floats is a bad idea. But what about implementing Try for |
I believe It seems the same also goes for |
user code can handle And I could see wanting to use |
Yes, partly because
|
I expect |
This is a tracking issue for the RFC "
try_trait_v2
: A new design for the?
desugaring" (rust-lang/rfcs#3058).The feature gate for the issue is
#![feature(try_trait_v2)]
.This obviates rust-lang/rfcs#1859, tracked in #42327.
About tracking issues
Tracking issues are used to record the overall progress of implementation.
They are also used as hubs connecting to other relevant issues, e.g., bugs or open design questions.
A tracking issue is however not meant for large scale discussion, questions, or bug reports about a feature.
Instead, open a dedicated issue for the specific matter and add the relevant feature gate label.
Steps
Delete the old way after a bootstrap updateRemove theTryV2
alias #88223?
desugaring #85133FromResidual
but notTry
FromResidual
better (Issue rustdoc removes Try from <Self as Try>::Residual in std::ops::FromResidual #85454)Infallible
are either fine that way or have been replaced by!
Iterator::try_fold
try_fold
for iterators for internal iteration #62606)fold
be implemented in terms oftry_fold
, so that both don't need to be overridden.)Unresolved Questions
From RFC:
Try
use in the associated types/traits? Output+residual, continue+break, or something else entirely?From experience in nightly:
FromResidual
from a type that's never actually produced as a residual (Fix the types in one of theFromResidual
implementations rwf2/Rocket#1645). But that would add more friction for cases not using theFoo<!>
pattern, so may not be worth it.array::{try_from_fn, try_map}
andIterator::try_find
generic overTry
#91286, that might look like changing the associatedtype Residual;
totype Residual: Residual<Self::Output>;
.Implementation history
try_trait_v2
library basics #84092try_trait
fromstdarch
, Remove#![feature(try_trait)]
from a test stdarch#1142try_trait_v2
#84767The text was updated successfully, but these errors were encountered: