pub trait ResultExt<T, E>: Sized {
// Required methods
fn lift<P>(
self,
) -> Result<T, Fail<P, <<E as IntoLanes>::Lanes as LaneProfile>::WithFatal>>
where E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection,
P: Rejection + Lift<<E as IntoLanes>::Rejected>,
<<E as IntoLanes>::Lanes as LaneProfile>::Fatal: Into<Fatal>,
<P as Lift<<E as IntoLanes>::Rejected>>::Unmapped: UnmappedInto<Fatal>;
fn narrow_rejected(self) -> Result<T, <E as NarrowRejectedLane>::Narrowed>
where E: NarrowRejectedLane;
fn narrow_transient(
self,
attempts: u32,
) -> Result<T, <E as Laned>::WithoutTransient>
where E: Laned;
fn narrow_denied(self) -> Result<T, <E as NarrowDeniedLane>::Narrowed>
where E: NarrowDeniedLane;
fn rejected(
self,
) -> Result<Result<T, <E as IntoLanes>::Rejected>, Fault<<E as IntoLanes>::Lanes>>
where E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection;
fn narrow_rejected_with(
self,
on_rejected: impl FnOnce(<E as IntoLanes>::Rejected) -> Result<T, <E as IntoLanes>::Rejected>,
) -> Result<T, Fault<<E as IntoLanes>::Lanes>>
where E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection,
<E as IntoLanes>::Lanes: LaneProfile<Fatal = Fatal>;
fn map_rejected<D2>(
self,
f: impl FnOnce(<E as IntoLanes>::Rejected) -> D2,
) -> Result<T, Fail<D2, <E as IntoLanes>::Lanes>>
where E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection;
fn into_fault(self) -> Result<T, Fault<<E as IntoLanes>::Lanes>>
where E: FaultSource;
fn into_fail(
self,
) -> Result<T, Fail<<E as IntoLanes>::Rejected, <E as IntoLanes>::Lanes>>
where E: FailSource;
fn classify<W>(self) -> Result<T, W>
where W: Classify + From<E>;
}Expand description
The one trait a consumer imports to move a Result between error
signatures: lift its rejection, narrow away a lane that is no
longer live at this boundary, hand the rejection to the caller as a value,
classify a foreign error into a local wrapper, or (under tracing) record
it onto the current span.
Required Methods§
Sourcefn lift<P>(
self,
) -> Result<T, Fail<P, <<E as IntoLanes>::Lanes as LaneProfile>::WithFatal>>
fn lift<P>( self, ) -> Result<T, Fail<P, <<E as IntoLanes>::Lanes as LaneProfile>::WithFatal>>
Lift a rejection into a new vocabulary and enable Fatal for any
unmapped case. ? subsequently expands the resulting lanes.
Sourcefn narrow_rejected(self) -> Result<T, <E as NarrowRejectedLane>::Narrowed>where
E: NarrowRejectedLane,
fn narrow_rejected(self) -> Result<T, <E as NarrowRejectedLane>::Narrowed>where
E: NarrowRejectedLane,
Narrows away the Rejected lane: a rejection with no caller left to
correct it becomes Fatal(Invariant), carrying the rejection as its
source. On a Fail<D, L> the result is Fault<L>. On a bare
Rejection — a public method that returns just R because its
caller can act on it, consumed by an internal frame that already
proved the precondition — the result is the bare Fatal, and ?
carries it into whatever Fault/Fail the function returns, so the
destination profile is never named at the call site:
use errlanes::{Fault, ResultExt, lanes};
#[derive(Debug, errlanes::Rejection)]
#[rejection(code = "LANE_DISABLED")]
struct LaneDisabled;
fn listen() -> Result<(), LaneDisabled> {
Err(LaneDisabled)
}
// The lane was required at registration: by now, off is an invariant.
fn run() -> Result<(), Fault<lanes!(Transient, Fatal)>> {
listen().narrow_rejected()?;
Ok(())
}
assert!(matches!(run(), Err(Fault::Fatal(_))));A Fault has no rejected lane to narrow, and that is a compile
error, not an identity:
use errlanes::{Fault, ResultExt, lanes};
fn narrow(value: Result<(), Fault<lanes!(Fatal)>>)
-> Result<(), Fault<lanes!(Fatal)>>
{
value.narrow_rejected()
}Sourcefn narrow_transient(
self,
attempts: u32,
) -> Result<T, <E as Laned>::WithoutTransient>where
E: Laned,
fn narrow_transient(
self,
attempts: u32,
) -> Result<T, <E as Laned>::WithoutTransient>where
E: Laned,
Narrows away the Transient lane: a caller-owned retry loop hands
back the narrowed profile once it stops retrying, so it stops
offering Transient to its own callers. attempts is what the loop
counted; an exhausted transient becomes Fatal(Exhausted) with the
last transient as its source.
Sourcefn narrow_denied(self) -> Result<T, <E as NarrowDeniedLane>::Narrowed>where
E: NarrowDeniedLane,
fn narrow_denied(self) -> Result<T, <E as NarrowDeniedLane>::Narrowed>where
E: NarrowDeniedLane,
Narrows away the Denied lane: a denial at a boundary with no
subject (code running as the system) becomes Fatal(Denied). Only
available where E’s profile enables Denied:
use errlanes::{Fault, ResultExt, lanes};
fn narrow(value: Result<(), Fault<lanes!(Transient, Fatal)>>)
-> Result<(), Fault<lanes!(Transient, Fatal)>>
{
value.narrow_denied()
}Sourcefn rejected(
self,
) -> Result<Result<T, <E as IntoLanes>::Rejected>, Fault<<E as IntoLanes>::Lanes>>where
E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection,
fn rejected(
self,
) -> Result<Result<T, <E as IntoLanes>::Rejected>, Fault<<E as IntoLanes>::Lanes>>where
E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection,
Hands the rejection to the caller as a value and keeps the faults
propagating: Result<T, Fail<D, L>> becomes
Result<Result<T, D>, Fault<L>>, the Result-level form of
Fail::rejected. The outer ? carries the faults on into any
enclosing carrier; the inner Result is the domain outcome, with the
rejection as its Err, matched right where it occurred:
use errlanes::{Fail, Fault, ResultExt, lanes};
#[derive(Debug, errlanes::Rejection)]
#[rejection(code = "TIMED_OUT")]
struct TimedOut;
fn await_completion() -> Result<u64, Fail<TimedOut, lanes!(Transient, Fatal)>> {
Err(Fail::Rejected(TimedOut))
}
fn poll_once() -> Result<Option<u64>, Fault<lanes!(Transient, Fatal)>> {
match await_completion().rejected()? {
Ok(outcome) => Ok(Some(outcome)),
Err(TimedOut) => Ok(None),
}
}
assert!(poll_once().unwrap().is_none());This is the dual of narrow_rejected: that
one is for a boundary with no caller left to correct the rejection,
this one for the call site that is going to. There is deliberately no
Option-returning accessor on a Result: an as_rejected() that
answered None for both Ok and a fault would be the one place a
lane could be dropped without naming it. Only available where E
carries a rejected lane; a Fault has none:
use errlanes::{Fault, ResultExt, lanes};
fn split(value: Result<(), Fault<lanes!(Fatal)>>) {
let _ = value.rejected();
}Sourcefn narrow_rejected_with(
self,
on_rejected: impl FnOnce(<E as IntoLanes>::Rejected) -> Result<T, <E as IntoLanes>::Rejected>,
) -> Result<T, Fault<<E as IntoLanes>::Lanes>>where
E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection,
<E as IntoLanes>::Lanes: LaneProfile<Fatal = Fatal>,
fn narrow_rejected_with(
self,
on_rejected: impl FnOnce(<E as IntoLanes>::Rejected) -> Result<T, <E as IntoLanes>::Rejected>,
) -> Result<T, Fault<<E as IntoLanes>::Lanes>>where
E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection,
<E as IntoLanes>::Lanes: LaneProfile<Fatal = Fatal>,
Narrows away the Rejected lane after the closure has had first
refusal: on_rejected receives the rejection by value and either
recovers with the success value (Ok) or hands back the member it
does not handle (Err), which is then narrowed exactly as
narrow_rejected would — Fatal(Invariant)
with the rejection as an opaque source. Faults already on the result
pass through untouched.
The closure’s match is the exhaustiveness proof: when every member
is recovered there is nothing left to narrow, and a member added
later is a compile error in the closure instead of a silent
Fatal(Invariant):
use errlanes::{Fail, Fault, ResultExt, lanes};
#[derive(Debug, errlanes::Rejection)]
#[rejection(code = "ALREADY_ADDED")]
struct AlreadyAdded;
fn add() -> Result<u32, Fail<AlreadyAdded, lanes!(Transient, Fatal)>> {
Err(Fail::Rejected(AlreadyAdded))
}
fn ensure_added() -> Result<(), Fault<lanes!(Transient, Fatal)>> {
add().map(|_| ()).narrow_rejected_with(|AlreadyAdded| Ok(()))
}
assert!(ensure_added().is_ok());Where some members are invariants, the closure hands them back:
use errlanes::{Fail, Fault, ResultExt, lanes};
#[derive(Debug, errlanes::Rejection)]
enum Outcome {
Missing,
Corrupt,
}
fn read(value: Result<u32, Fail<Outcome, lanes!(Fatal)>>) -> Result<u32, Fault<lanes!(Fatal)>> {
value.narrow_rejected_with(|r| match r {
Outcome::Missing => Ok(0),
Outcome::Corrupt => Err(r),
})
}
assert_eq!(read(Err(Fail::Rejected(Outcome::Missing))).unwrap(), 0);
assert!(matches!(read(Err(Fail::Rejected(Outcome::Corrupt))), Err(Fault::Fatal(_))));Sourcefn map_rejected<D2>(
self,
f: impl FnOnce(<E as IntoLanes>::Rejected) -> D2,
) -> Result<T, Fail<D2, <E as IntoLanes>::Lanes>>where
E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection,
fn map_rejected<D2>(
self,
f: impl FnOnce(<E as IntoLanes>::Rejected) -> D2,
) -> Result<T, Fail<D2, <E as IntoLanes>::Lanes>>where
E: IntoLanes,
<E as IntoLanes>::Rejected: Rejection,
Maps the rejection with a closure and leaves every other lane as it
is: the Result-level form of Fail::map_rejected. For a
type-level remap use lift; this is for enriching a
rejection with data only the call site has, such as the input that
was attempted: repo.create(new).await.map_rejected(|r| r.with_attempted(id))?.
Sourcefn into_fault(self) -> Result<T, Fault<<E as IntoLanes>::Lanes>>where
E: FaultSource,
fn into_fault(self) -> Result<T, Fault<<E as IntoLanes>::Lanes>>where
E: FaultSource,
Convert a never-rejecting source to its own Fault at a box or carrier boundary.
Sourcefn into_fail(
self,
) -> Result<T, Fail<<E as IntoLanes>::Rejected, <E as IntoLanes>::Lanes>>where
E: FailSource,
fn into_fail(
self,
) -> Result<T, Fail<<E as IntoLanes>::Rejected, <E as IntoLanes>::Lanes>>where
E: FailSource,
Convert a rejecting source to its own Fail at a foreign carrier boundary.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety".