Skip to main content

ResultExt

Trait ResultExt 

Source
pub trait ResultExt<T, E>: Sized {
    // Required methods
    fn widen<E2>(self) -> Result<T, E2>
       where Self: WidenResult<T, E2>;
    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 Failure>::Rejection>, Fault<<E as Failure>::Lanes>>
       where E: Failure;
    fn map_rejected<D2>(
        self,
        f: impl FnOnce(<E as Failure>::Rejection) -> D2,
    ) -> Result<T, Fail<D2, <E as Failure>::Lanes>>
       where E: Failure;
    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: widen it to a bigger profile, 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§

Source

fn widen<E2>(self) -> Result<T, E2>
where Self: WidenResult<T, E2>,

Target-inferred widening for Fault or Fail results.

Fault results widen to Fault; Fail results widen to Fail, converting the rejection through crate::Lift. Success values and fault payloads are preserved. Widening can add lanes, but cannot silently discard an enabled lane:

ⓘ
use errlanes::{Fault, ResultExt, lanes};
fn discard_denied(value: Result<(), Fault<lanes!(Denied, Fatal)>>)
    -> Result<(), Fault<lanes!(Fatal)>>
{
    value.widen()
}

A Classify wrapper widens too — into a Fail as usual, or straight into a Fault when it never rejects. That last form is how a wrapper enters the lanes at a call site with no signature to infer the destination from, above all on the way into a Box<dyn Error>, where ? on the bare wrapper would box it unlaned and lose its classification (see classify.rs and the README’s box-boundary section).

Source

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()
}
Source

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.

Source

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()
}
Source

fn rejected( self, ) -> Result<Result<T, <E as Failure>::Rejection>, Fault<<E as Failure>::Lanes>>
where E: Failure,

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();
}
Source

fn map_rejected<D2>( self, f: impl FnOnce(<E as Failure>::Rejection) -> D2, ) -> Result<T, Fail<D2, <E as Failure>::Lanes>>
where E: Failure,

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 widen; 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))?.

Source

fn classify<W>(self) -> Result<T, W>
where W: Classify + From<E>,

.classify::<W>() — the verb that turns a foreign error into a local Classify wrapper at a one-off call site, so a function that does not itself return W can still enter the lanes through it: conn.query(..).await.classify::<DbWrite>()?.

Dyn Compatibility§

This trait is not dyn compatible.

In older versions of Rust, dyn compatibility was called "object safety".

Implementations on Foreign Types§

Source§

impl<T, E> ResultExt<T, E> for Result<T, E>

Source§

fn widen<E2>(self) -> Result<T, E2>
where Result<T, E>: WidenResult<T, E2>,

Source§

fn narrow_rejected(self) -> Result<T, <E as NarrowRejectedLane>::Narrowed>
where E: NarrowRejectedLane,

Source§

fn narrow_transient( self, attempts: u32, ) -> Result<T, <E as Laned>::WithoutTransient>
where E: Laned,

Source§

fn narrow_denied(self) -> Result<T, <E as NarrowDeniedLane>::Narrowed>
where E: NarrowDeniedLane,

Source§

fn rejected( self, ) -> Result<Result<T, <E as Failure>::Rejection>, Fault<<E as Failure>::Lanes>>
where E: Failure,

Source§

fn map_rejected<D2>( self, f: impl FnOnce(<E as Failure>::Rejection) -> D2, ) -> Result<T, Fail<D2, <E as Failure>::Lanes>>
where E: Failure,

Source§

fn classify<W>(self) -> Result<T, W>
where W: Classify + From<E>,

Implementors§