Outcome

Struct Outcome 

Source
pub struct Outcome<T, E> { /* private fields */ }
Expand description

Contains a value, and any errors produced while obtaining that value.

Outcome<T> can be used like a Result<T, Vec<E>>, except it has both the Ok and Err variants at the same time. This is useful for modelling procedures which should try to accumulate as many errors as possible before failing, even if fatal, such as parsing.

§Creation

Instances of Outcome can be created in a number of different ways, depending on what you are trying to achieve, and how you are producing errors.

Use new_with_errors to construct “raw” from an existing value and list of errors:

Outcome::new_with_errors(42, vec!["something that went wrong"]);

Use build to compute a value while accumulating errors:

Outcome::build(|errs| {
    let mut sum = 0;
    for i in 0..10 {
        if i % 3 == 0 {
            errs.push_error("don't like multiples of 3");
        }
        sum += i;
    }
    sum
});

§Finalization

If you have an Outcome and need to get the value and errors out, call finalize. This gives both the inner value and an ErrorSentinel, a special type which ensures that the errors are handled in some way before it is dropped. If the errors are unhandled, it will cause a panic to alert you to your logic error. See the documentation for ErrorSentinel for details.

fn something() -> Outcome<u32, String> {
    // ...
}
 
let o = something();
let (value, errors) = o.finalize();
 
println!("value is {value}");
 
// This iteration counts as handling the error, as per the `ErrorSentinel::into_errors_iter`
// docs. If we didn't handle the errors, using this method or some other one, our program
// would panic when `errors` was dropped.
for err in errors.into_errors_iter() {
    println!("error: {err}");
}

§Combination

Outcome provides some functional combinators to transform and combine instances together. These are useful for modularizing complex pieces of functionality which could all produce errors individually, but which you will need to collect together later.

  • Transform values and/or errors: map, map_errors
  • Unwrap a value by moving its errors elsewhere: propagate
  • Fold two values and combine their errors: integrate
  • Bundle values into a collection and combine their errors: zip, from_iter
  • Extract the value by asserting there are no errors: unwrap, expect

Implementations§

Source§

impl<T, E> Outcome<T, E>

Source

pub fn new(value: T) -> Self

Constructs a new Outcome with a value and no errors.

let mut o = Outcome::new(42);
assert_eq!(o.len_errors(), 0);
Source

pub fn new_with_errors(value: T, errors: Vec<E>) -> Self

Constructs a new Outcome with some errors.

let mut o = Outcome::new_with_errors(42, vec!["an error"]);
assert_eq!(o.len_errors(), 1);
Source

pub fn build<F>(func: F) -> Self
where F: FnOnce(&mut ErrorSentinel<E>) -> T,

A convenience function to construct a new Outcome by accumulating errors over time, and finally returning some value.

fn sub_task() -> Outcome<u32, String> {
    Outcome::new_with_errors(
        42,
        vec!["struggled to compute meaning of life".to_owned()]
    )
}
 
let o = Outcome::build(|errs| {
    // Produce some errors of our own...
    errs.push_error("what are we doing?".to_owned());
 
    // ...or propagate errors from another `Outcome`
    let value = sub_task().propagate(errs);
 
    value + 1
});
 
let (value, errors) = o.finalize();
assert_eq!(value, 42 + 1);
assert_eq!(errors.len(), 2);
Source

pub fn push_error(&mut self, error: E)

Adds a new error to this Outcome.

let mut o = Outcome::new(42);
o.push_error("oh no!");
 
assert!(o.has_errors());
Source

pub fn propagate(self, other: &mut impl ErrorCollector<E>) -> T

Moves the errors from this Outcome into an ErrorCollector, and unwraps it to return its value.

let mut source = Outcome::new(42);
source.push_error("oh no!");
source.push_error("another error!");
let mut dest = Outcome::new(123);
dest.push_error("one last failure!");
 
let source_value = source.propagate(&mut dest);
assert_eq!(dest.len_errors(), 3);
assert_eq!(source_value, 42);
Source

pub fn integrate<OT>( self, other: &mut Outcome<OT, E>, func: impl FnOnce(&mut OT, T), )

Moves the errors from this Outcome into another Outcome, and apply a mapping function to transform the value within that Outcome based on the value within this one.

let mut source = Outcome::new(42);
source.push_error("oh no!");
source.push_error("another error!");
let mut dest = Outcome::new(123);
dest.push_error("one last failure!");
 
// Integrate by adding the values
source.integrate(&mut dest, |acc, x| *acc += x);
 
// Check result
let (value, errors) = dest.finalize();
assert_eq!(value, 123 + 42);
assert_eq!(errors.len(), 3);
Source

pub fn zip<OT>(self, other: Outcome<OT, E>) -> Outcome<(T, OT), E>

Consumes this Outcome and another one, returning a new Outcome with their values as a tuple (this, other) and the errors combined.

let a = Outcome::new_with_errors(5, vec!["error 1", "error 2"]);
let b = Outcome::new_with_errors(9, vec!["error 3"]);
 
let zipped = a.zip(b);
 
let (value, errors) = zipped.finalize();
assert_eq!(value, (5, 9));
assert_eq!(errors.len(), 3);
Source

pub fn map<R>(self, func: impl FnOnce(T) -> R) -> Outcome<R, E>

Applies a function to the value within this Outcome.

let o = Outcome::new_with_errors("Hello".to_owned(), vec!["oh no!"]);
let o_rev = o.map(|s| s.len());
 
let (value, errors) = o_rev.finalize();
assert_eq!(value, 5);
assert_eq!(errors.len(), 1);
Source

pub fn map_errors<R>(self, func: impl FnMut(E) -> R) -> Outcome<T, R>

Applies a function to the errors within this Outcome.

let o = Outcome::new_with_errors(42, vec!["oh no!", "something went wrong"]);
let o_mapped = o.map_errors(|e| e.to_uppercase());
 
let (value, errors) = o_mapped.finalize();
assert_eq!(value, 42);
assert_eq!(errors.peek(), &["OH NO!".to_owned(), "SOMETHING WENT WRONG".to_owned()]);
Source

pub fn unwrap(self) -> T
where E: Debug,

Extracts the inner value, panicking if there are any errors.

The panic message includes the Debug representation of the errors. If you would like to provide a custom message instead, use expect.

let o = Outcome::new_with_errors(42, vec!["error 1", "error 2"]);
o.unwrap(); // Panics
let o: Outcome<_, String> = Outcome::new(42);
let value = o.unwrap();
assert_eq!(value, 42);
Source

pub fn expect(self, msg: &str) -> T
where E: Debug,

Extracts the inner value, panicking with a message if there are any errors.

let o = Outcome::new_with_errors(42, vec!["error 1", "error 2"]);
o.expect("something went wrong"); // Panics
let o: Outcome<_, String> = Outcome::new(42);
let value = o.expect("something went wrong");
assert_eq!(value, 42);
Source

pub fn into_result(self) -> Result<T, ErrorSentinel<E>>

Converts this Outcome into a Result:

  • If there are no errors, produces an Ok with the value.
  • Otherwise, produces an Err with an ErrorSentinel, discarding the value. This means you must handle the errors before they are dropped, as with finalize.
Source

pub fn into_errors(self) -> ErrorSentinel<E>

Converts this Outcome into an ErrorSentinel, discarding the value.

You must handle the errors before they are dropped, as with finalize.

Source

pub fn has_errors(&self) -> bool

Returns true if this Outcome has any errors.

Opposite of is_success.

Source

pub fn is_success(&self) -> bool

Returns true if this Outcome has no errors.

Opposite of has_errors.

Source

pub fn len_errors(&self) -> usize

The number of errors within this Outcome.

let mut o = Outcome::new(42);
o.push_error("this went wrong");
o.push_error("that went wrong");
 
assert_eq!(o.len_errors(), 2);
Source

pub fn finalize(self) -> (T, ErrorSentinel<E>)

Consumes and deconstructs this Outcome into its value and an ErrorSentinel.

The ErrorSentinel verifies that any errors are handled before it is dropped, most likely by calling handle. Failure to do this will cause a panic, even if there were no errors. See the ErrorSentinel docs for more details.

let mut o = Outcome::new(42);
o.push_error("this went wrong");

let (value, errors) = o.finalize();
assert_eq!(value, 42);
 
errors.handle(|errs| {
    for err in &errs {
        println!("error: {err}");
    }
    assert_eq!(errs.len(), 1);
});

Trait Implementations§

Source§

impl<T: Clone, E: Clone> Clone for Outcome<T, E>

Source§

fn clone(&self) -> Outcome<T, E>

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<T: Debug, E: Debug> Debug for Outcome<T, E>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<T, E> ErrorCollector<E> for Outcome<T, E>

Source§

type WrappedInner = T

The type returned by propagate.
Source§

fn push_error(&mut self, error: E)

Add a new error to the collection of errors.
Source§

fn propagate(self, other: &mut impl ErrorCollector<E>) -> Self::WrappedInner

Consumes this collector and pushes all of its errors into a different collector. If the type is wrapping some kind of value, it may return it too.
Source§

impl<T, E, C: FromIterator<T>> FromIterator<Outcome<T, E>> for Outcome<C, E>

Source§

fn from_iter<I: IntoIterator<Item = Outcome<T, E>>>(iter: I) -> Self

Enables an Iterator of Outcome items to be converted into a single Outcome whose item is a collection containing each of the items’ values.

The errors are aggregated in order.

let items = vec![
    Outcome::new_with_errors(1, vec!["error 1", "error 2"]),
    Outcome::new_with_errors(2, vec!["error 3"]),
    Outcome::new_with_errors(3, vec!["error 4", "error 5"]),
];
 
let combined: Outcome<Vec<u32>, _> = items.into_iter().collect();
 
let (value, errors) = combined.finalize();
assert_eq!(value, vec![1, 2, 3]);
assert_eq!(errors.len(), 5);
Source§

impl<T: Hash, E: Hash> Hash for Outcome<T, E>

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl<T: PartialEq, E: PartialEq> PartialEq for Outcome<T, E>

Source§

fn eq(&self, other: &Outcome<T, E>) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl<T: Eq, E: Eq> Eq for Outcome<T, E>

Source§

impl<T, E> StructuralPartialEq for Outcome<T, E>

Auto Trait Implementations§

§

impl<T, E> Freeze for Outcome<T, E>
where T: Freeze,

§

impl<T, E> RefUnwindSafe for Outcome<T, E>

§

impl<T, E> Send for Outcome<T, E>
where T: Send, E: Send,

§

impl<T, E> Sync for Outcome<T, E>
where T: Sync, E: Sync,

§

impl<T, E> Unpin for Outcome<T, E>
where T: Unpin, E: Unpin,

§

impl<T, E> UnwindSafe for Outcome<T, E>
where T: UnwindSafe, E: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.