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.
Implementations§
Source§impl<T, E> Outcome<T, E>
impl<T, E> Outcome<T, E>
Sourcepub fn new(value: T) -> Self
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);Sourcepub fn new_with_errors(value: T, errors: Vec<E>) -> Self
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);Sourcepub fn build<F>(func: F) -> Selfwhere
F: FnOnce(&mut ErrorSentinel<E>) -> T,
pub fn build<F>(func: F) -> Selfwhere
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);Sourcepub fn push_error(&mut self, error: E)
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());Sourcepub fn propagate(self, other: &mut impl ErrorCollector<E>) -> T
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);Sourcepub fn integrate<OT>(
self,
other: &mut Outcome<OT, E>,
func: impl FnOnce(&mut OT, T),
)
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);Sourcepub fn zip<OT>(self, other: Outcome<OT, E>) -> Outcome<(T, OT), E>
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);Sourcepub fn map<R>(self, func: impl FnOnce(T) -> R) -> Outcome<R, E>
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);Sourcepub fn map_errors<R>(self, func: impl FnMut(E) -> R) -> Outcome<T, R>
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()]);Sourcepub fn unwrap(self) -> Twhere
E: Debug,
pub fn unwrap(self) -> Twhere
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(); // Panicslet o: Outcome<_, String> = Outcome::new(42);
let value = o.unwrap();
assert_eq!(value, 42);Sourcepub fn expect(self, msg: &str) -> Twhere
E: Debug,
pub fn expect(self, msg: &str) -> Twhere
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"); // Panicslet o: Outcome<_, String> = Outcome::new(42);
let value = o.expect("something went wrong");
assert_eq!(value, 42);Sourcepub fn into_result(self) -> Result<T, ErrorSentinel<E>>
pub fn into_result(self) -> Result<T, ErrorSentinel<E>>
Converts this Outcome into a Result:
- If there are no errors, produces an
Okwith the value. - Otherwise, produces an
Errwith anErrorSentinel, discarding the value. This means you must handle the errors before they are dropped, as withfinalize.
Sourcepub fn into_errors(self) -> ErrorSentinel<E>
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.
Sourcepub fn has_errors(&self) -> bool
pub fn has_errors(&self) -> bool
Returns true if this Outcome has any errors.
Opposite of is_success.
Sourcepub fn is_success(&self) -> bool
pub fn is_success(&self) -> bool
Returns true if this Outcome has no errors.
Opposite of has_errors.
Sourcepub fn len_errors(&self) -> usize
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);Sourcepub fn finalize(self) -> (T, ErrorSentinel<E>)
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, E> ErrorCollector<E> for Outcome<T, E>
impl<T, E> ErrorCollector<E> for Outcome<T, E>
Source§type WrappedInner = T
type WrappedInner = T
propagate.Source§fn push_error(&mut self, error: E)
fn push_error(&mut self, error: E)
Source§fn propagate(self, other: &mut impl ErrorCollector<E>) -> Self::WrappedInner
fn propagate(self, other: &mut impl ErrorCollector<E>) -> Self::WrappedInner
Source§impl<T, E, C: FromIterator<T>> FromIterator<Outcome<T, E>> for Outcome<C, E>
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
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);