Try in std::ops - Rust
pub trait Try: FromResidual {
    type Output;
    type Residual;
    // Required methods
    fn from_output(output: Self::Output) -> Self;
    fn branch(self) -> ControlFlow<Self::Residual, Self::Output>;
}
🔬This is a nightly-only experimental API. ( try_trait_v2 #84277)
Expand description
The ? operator and try {} blocks.
try_* methods typically involve a type implementing this trait. For
example, the closures passed to Iterator::try_fold and
Iterator::try_for_each must return such a type.
Try types are typically those containing two or more categories of values,
some subset of which are so commonly handled via early returns that it’s
worth providing a terse (but still visible) syntax to make that easy.
This is most often seen for error handling with Result and Option.
The quintessential implementation of this trait is on ControlFlow.
§ Using Try in Generic Code
Iterator::try_fold was stabilized to call back in Rust 1.27, but
this trait is much newer. To illustrate the various associated types and
methods, let’s implement our own version.
As a reminder, an infallible version of a fold looks something like this:
fn simple_fold<A, T>(
    iter: impl Iterator<Item = T>,
    mut accum: A,
    mut f: impl FnMut(A, T) -> A,
) -> A {
    for x in iter {
        accum = f(accum, x);
    }
    accum
}
So instead of f returning just an A, we’ll need it to return some other
type that produces an A in the “don’t short circuit” path. Conveniently,
that’s also the type we need to return from the function.
Let’s add a new generic parameter R for that type, and bound it to the
output type that we want:
fn simple_try_fold_1<A, T, R: Try<Output = A>>(
    iter: impl Iterator<Item = T>,
    mut accum: A,
    mut f: impl FnMut(A, T) -> R,
) -> R {
    todo!()
}
If we get through the entire iterator, we need to wrap up the accumulator
into the return type using Try::from_output:
fn simple_try_fold_2<A, T, R: Try<Output = A>>(
    iter: impl Iterator<Item = T>,
    mut accum: A,
    mut f: impl FnMut(A, T) -> R,
) -> R {
    for x in iter {
        let cf = f(accum, x).branch();
        match cf {
            ControlFlow::Continue(a) => accum = a,
            ControlFlow::Break(_) => todo!(),
        }
    }
    R::from_output(accum)
}
We’ll also need FromResidual::from_residual to turn the residual back
into the original type. But because it’s a supertrait of Try, we don’t
need to mention it in the bounds. All types which implement Try can be
recreated from their corresponding residual, so we’ll just call it:
pub fn simple_try_fold_3<A, T, R: Try<Output = A>>(
    iter: impl Iterator<Item = T>,
    mut accum: A,
    mut f: impl FnMut(A, T) -> R,
) -> R {
    for x in iter {
        let cf = f(accum, x).branch();
        match cf {
            ControlFlow::Continue(a) => accum = a,
            ControlFlow::Break(r) => return R::from_residual(r),
        }
    }
    R::from_output(accum)
}
But this “call branch, then match on it, and return if it was a
Break” is exactly what happens inside the ? operator. So rather than
do all this manually, we can just use ? instead:
fn simple_try_fold<A, T, R: Try<Output = A>>(
    iter: impl Iterator<Item = T>,
    mut accum: A,
    mut f: impl FnMut(A, T) -> R,
) -> R {
    for x in iter {
        accum = f(accum, x)?;
    }
    R::from_output(accum)
}
Source
🔬This is a nightly-only experimental API. ( try_trait_v2 #84277)
The type of the value produced by ? when not short-circuiting.
Source
🔬This is a nightly-only experimental API. ( try_trait_v2 #84277)
The type of the value passed to FromResidual::from_residual
as part of ? when short-circuiting.
This represents the possible values of the Self type which are not
represented by the Output type.
§ Note to Implementors
The choice of this type is critical to interconversion.
Unlike the Output type, which will often be a raw generic type,
this type is typically a newtype of some sort to “color” the type
so that it’s distinguishable from the residuals of other types.
This is why Result<T, E>::Residual is not E, but Result<Infallible, E>.
That way it’s distinct from ControlFlow<E>::Residual, for example,
and thus ? on ControlFlow cannot be used in a method returning Result.
If you’re making a generic type Foo<T> that implements Try<Output = T>,
then typically you can use Foo<std::convert::Infallible> as its Residual
type: that type will have a “hole” in the correct place, and will maintain the
“foo-ness” of the residual so other types need to opt-in to interconversion.
Source
🔬This is a nightly-only experimental API. ( try_trait_v2 #84277)
Constructs the type from its Output type.
This should be implemented consistently with the branch method
such that applying the ? operator will get back the original value:
Try::from_output(x).branch() --> ControlFlow::Continue(x).
§ Examples
#![feature(try_trait_v2)]
use std::ops::Try;
assert_eq!(<Result<_, String> as Try>::from_output(3), Ok(3));
assert_eq!(<Option<_> as Try>::from_output(4), Some(4));
assert_eq!(
    <std::ops::ControlFlow<String, _> as Try>::from_output(5),
    std::ops::ControlFlow::Continue(5),
);
assert_eq!(Option::from_output(4)?, 4);
// This is used, for example, on the accumulator in `try_fold`:
let r = std::iter::empty().try_fold(4, |_, ()| -> Option<_> { unreachable!() });
assert_eq!(r, Some(4));
Source
🔬This is a nightly-only experimental API. ( try_trait_v2 #84277)
Used in ? to decide whether the operator should produce a value
(because this returned ControlFlow::Continue)
or propagate a value back to the caller
(because this returned ControlFlow::Break).
§ Examples
#![feature(try_trait_v2)]
use std::ops::{ControlFlow, Try};
assert_eq!(Ok::<_, String>(3).branch(), ControlFlow::Continue(3));
assert_eq!(Err::<String, _>(3).branch(), ControlFlow::Break(Err(3)));
assert_eq!(Some(3).branch(), ControlFlow::Continue(3));
assert_eq!(None::<String>.branch(), ControlFlow::Break(None));
assert_eq!(ControlFlow::<String, _>::Continue(3).branch(), ControlFlow::Continue(3));
assert_eq!(
    ControlFlow::<_, String>::Break(3).branch(),
    ControlFlow::Break(ControlFlow::Break(3)),
);
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.
