You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Experiment: Introduce Try::Kind and TryAs - #163848
This is an experiment to re-design the Try trait. The elevator pitch is that "residual types" like Result<!, E> becomes just E. And Try::Kind is the new source of "what kind of residual is this?". Residual trait is replaced with TryAs, which uses the try type instead of the residual to transform between similar try types.
Custom try types can re-use std try kind values like TryResult to make a type that is inter-operable with Result with one instead of two TryFromBreak impls.
pubtraitTry:TryFromBreak<Self::Break,Self::Kind>{/// A marker type to signify the general meaning of the `?` operation on this type./// For example, `<Result as Try>::Kind = TryResult`, and `TryResult` should be used for/// custom "result-like" types which may contain an error./// When using `?` within a function, the return type of the function must implement/// `TryFromBreak<T, <T as Try>::Kind>` where `T` is the type of the target of `?`.typeKind;/// The type of the value produced by `?` when *not* short-circuiting.typeOutput;/// The type of the value produced by `?` when short-circuiting.typeBreak;fnfrom_output(output:Self::Output) -> Self;/// Used in `?` to determine if it should continue with a value or short-circuit with a value.fnbranch(self) -> ControlFlow<Self::Break,Self::Output>;}/// Used to specify which residuals can be converted into which [`Try`] types.pubtraitTryFromBreak<B = <SelfasTry>::Break,Kind = <SelfasTry>::Kind>{fnfrom_break(b:B) -> Self;}/// The `TryAs` trait is used to derive a type that is equivalent but with/// `Try::Output` changed to `T`.pubtraitTryAs<T>:Try{/// The Try type that is similar to Self, but with `Output` changed to `T`typeTry:Try<Kind = Self::Kind,Output = T,Break = Self::Break>;}
Advantages
Quadratic to linear FromResidual impl count for a family of Try types
Suppose you have 4 custom Result-like types (they produce a T or an E) and you want all of them to be inter-convertible with each other in try contexts. With try_trait_v2, you would need 12 FromResidual impls (n(n - 1) - one for each pair of types, both directions). With Kind and TryFromBreak, you would only need 4 TryFromBreak impls.
try_trait_v2:
impl<T, E> FromResidual<ResultOneResidual<E>> for ResultOne<T, E>
impl<T, E> FromResidual<ResultTwoResidual<E>> for ResultTwo<T, E>
impl<T, E> FromResidual<ResultOneResidual<E>> for ResultTwo<T, E>
impl<T, E> FromResidual<ResultTwoResidual<E>> for ResultOne<T, E>
This proposal:
impl<T, E> TryFromBreak<E, TryResult> for ResultOne<T, E>
impl<T, E> TryFromBreak<E, TryResult> for ResultTwo<T, E>
No residual "unwrapping"
try_trait_v2:
impl<T,E>FromResidualforResult<T,E>{fnfrom_residual(r:Result<!,E>){match r {Err(e) => Err(e),}}}
The yeet keyword can simply use the TryFromBreak impl that is required by the Try trait. It does not need any dedicatedTryFromBreak impls nor a dedicated Try::Kind. You just yeet a Break value and it will be interpreted as having the Try::Kind of the current try scope. In try_trait_v2, yeet has a dedicated residual type and requires FromResidual impls to use it.
"kinds" is more teachable than "residual types"
A try kind is a family of inter-convertible try types that can all be used within a try context of that kind. There is no unifying concept like this with residual types.
Residual types are difficult concept to understand and teach. It's not obvious what information a residual type is intended to communicate exactly. You might think that a custom result-like type should use Result<!, E> as its residual type, but that would be wrong. Try "kinds" provide better separation of concerns - the Kind and the Break are separated. Try kinds like TryResult can be documented and explained as a standalone concept.
rustbot
added
A-attributes
Area: Attributes (`#[…]`, `#![…]`)
S-waiting-on-author
Status: This is awaiting some action (such as code changes or more information) from the author.
T-clippy
Relevant to the Clippy team.
T-compiler
Relevant to the compiler team, which will review and decide on the PR/issue.
T-libs
Relevant to the library team, which will review and decide on the PR/issue.
T-rustdoc
Relevant to the rustdoc team, which will review and decide on the PR/issue.
T-rustdoc-frontend
Relevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output.
WG-trait-system-refactor
The Rustc Trait System Refactor Initiative (-Znext-solver)
labels
Oct 6, 2026
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
A-attributesArea: Attributes (`#[…]`, `#![…]`)S-waiting-on-authorStatus: This is awaiting some action (such as code changes or more information) from the author.T-clippyRelevant to the Clippy team.T-compilerRelevant to the compiler team, which will review and decide on the PR/issue.T-libsRelevant to the library team, which will review and decide on the PR/issue.T-rustdocRelevant to the rustdoc team, which will review and decide on the PR/issue.T-rustdoc-frontendRelevant to the rustdoc-frontend team, which will review and decide on the web UI/UX output.WG-trait-system-refactorThe Rustc Trait System Refactor Initiative (-Znext-solver)
4 participants
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This is an experiment to re-design the Try trait. The elevator pitch is that "residual types" like
Result<!, E>becomes justE. AndTry::Kindis the new source of "what kind of residual is this?".Residualtrait is replaced withTryAs, which uses the try type instead of the residual to transform between similar try types.Custom try types can re-use std try kind values like
TryResultto make a type that is inter-operable withResultwith one instead of twoTryFromBreakimpls.See also https://internals.rust-lang.org/t/pre-rfc-fat-try-another-attempt-to-re-design-the-try-trait/24618
The traits
Advantages
Quadratic to linear
FromResidualimpl count for a family of Try typesSuppose you have 4 custom Result-like types (they produce a T or an E) and you want all of them to be inter-convertible with each other in try contexts. With
try_trait_v2, you would need 12FromResidualimpls (n(n - 1)- one for each pair of types, both directions). WithKindandTryFromBreak, you would only need 4TryFromBreakimpls.try_trait_v2:impl<T, E> FromResidual<ResultOneResidual<E>> for ResultOne<T, E>impl<T, E> FromResidual<ResultTwoResidual<E>> for ResultTwo<T, E>impl<T, E> FromResidual<ResultOneResidual<E>> for ResultTwo<T, E>impl<T, E> FromResidual<ResultTwoResidual<E>> for ResultOne<T, E>This proposal:
impl<T, E> TryFromBreak<E, TryResult> for ResultOne<T, E>impl<T, E> TryFromBreak<E, TryResult> for ResultTwo<T, E>No residual "unwrapping"
try_trait_v2:This propsal:
Simple
yeetsemanticsThe
yeetkeyword can simply use theTryFromBreakimpl that is required by theTrytrait. It does not need any dedicatedTryFromBreakimpls nor a dedicatedTry::Kind. You just yeet aBreakvalue and it will be interpreted as having theTry::Kindof the current try scope. Intry_trait_v2,yeethas a dedicated residual type and requiresFromResidualimpls to use it."kinds" is more teachable than "residual types"
A try kind is a family of inter-convertible try types that can all be used within a try context of that kind. There is no unifying concept like this with residual types.
Residual types are difficult concept to understand and teach. It's not obvious what information a residual type is intended to communicate exactly. You might think that a custom result-like type should use
Result<!, E>as its residual type, but that would be wrong. Try "kinds" provide better separation of concerns - theKindand theBreakare separated. Try kinds likeTryResultcan be documented and explained as a standalone concept.Function signatures with
TryAsExample with
Arc::try_mapBefore:
After:
r? @MusicalNinjaDad