ErrorSentinel

Struct ErrorSentinel 

Source
pub struct ErrorSentinel<E> { /* private fields */ }
Expand description

Represents errors which must be handled before this sentinel is dropped.

ErrorSentinel has a custom implementation of the Drop trait which checks that the errors were handled in some way, and panics if not.

{
    let errors = ErrorSentinel::new(vec!["error 1", "error 2"]);
    // Panic occurs here!
}

Using a method which marks the errors as handled will suppress the panic:

{
    let errors = ErrorSentinel::new(vec!["error 1", "error 2"]);
    errors.handle(|errs| {
        for err in errs {
            println!("error: {err}");
        }    
    });
    // No panic, because errors were handled
}

Methods which consider the errors “handled” are documented as such, and all also consume the ErrorSentinel. Most often, you will want to do one of the following:

  • Handle errors with some custom logic: handle
  • Assert that there are no errors: unwrap or expect
  • Delegate the errors to another object: propagate

In addition, an ErrorSentinel can be used as a generic container to accumulate errors during an operation, which can then be returned as an Outcome. See into_outcome and Outcome::build.

Implementations§

Source§

impl<E> ErrorSentinel<E>

Source

pub fn new(errors: Vec<E>) -> Self

Constructs a new unhandled ErrorSentinel with errors.

Usually this is not needed to be called manually, and an ErrorSentinel will be created by Outcome::finalize instead.

Source

pub fn empty() -> Self

Constructs a new unhandled ErrorSentinel without any errors.

Source

pub fn handle<R>(self, handler: impl FnOnce(Vec<E>) -> R) -> R

Handles the errors by executing a closure, returning the value which it evaluates to.

let errors = ErrorSentinel::new(vec!["error 1", "error 2", "error 3"]);
 
let mut error_count = 0;
errors.handle(|errs| {
    for err in errs {
        println!("error: {err}");
        error_count += 1;
    }    
});
 
assert_eq!(error_count, 3);

The closure should implement appropriate error-handling logic for your application, such as printing messages.

ErrorSentinel’s checking can only go so far, and does not understand if you have actually done something appropriate with the errors. This would be considered a valid handling of errors:

errors.handle(|_| ());
Source

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

Handles the errors by moving them into an ErrorCollector, effectively postponing them to be handled later instead.

let source = ErrorSentinel::new(vec!["error 1", "error 2"]);
let mut dest = ErrorSentinel::new(vec!["error 3", "error 4", "error 5"]);
 
source.propagate(&mut dest);
assert_eq!(dest.peek().len(), 5);
Source

pub fn ignore(self)

Handles the errors by ignoring them, dropping the list of errors.

let errors = ErrorSentinel::new(vec!["error 1", "error 2", "error 3"]);
errors.ignore();

This exists as an “escape hatch”, but its use is strongly not recommended. There is probably a more suitable method for what you are trying to do. Consider using unwrap or expect if there should not be any errors in the ErrorSentinel, which will panic if this assumption is violated unlike ignore.

Source

pub fn into_outcome<T>(self, value: T) -> Outcome<T, E>

Handles the errors by moving them into a new Outcome with a given value.

let errors = ErrorSentinel::new(vec!["error 1", "error 2", "error 3"]);
let outcome = errors.into_outcome(42);
 
assert_eq!(outcome.len_errors(), 3);

This can be useful for performing some logic which accumulates errors over time, and then finally creating an Outcome to return with a calculated value. Using an ErrorSentinel to accumulate the errors ensures that you cannot forget to return them.

See also Outcome::build, which provides a closure-based helper for the same pattern.

/// Sum the integer values in a sequence of strings.
/// Any non-integer values are returned as errors.
pub fn sum_ints<'a>(input: &[&'a str]) -> Outcome<u32, &'a str> {
    let mut errors = ErrorSentinel::empty();
    let mut sum = 0;
 
    for item in input {
        match item.parse::<u32>() {
            Ok(num) => sum += num,
            Err(_) => errors.push_error(*item),
        }
    }
 
    errors.into_outcome(sum)
}
 
let result = sum_ints(&["12", "a", "5", "b", "c", "2"]);
let (value, errors) = result.finalize();
 
assert_eq!(value, 12 + 5 + 2);
assert_eq!(errors.peek(), &["a", "b", "c"]);
Source

pub fn into_errors_iter(self) -> ErrorSentinelIter<E>

Consumes this ErrorSentinel to create an ErrorSentinelIter, enabling errors to be handled as an iterator.

The iterator must be entirely consumed to consider the errors handled, else the iterator will panic on drop. Refer to the type-level documentation for ErrorSentinelIter for more details.

This is deliberately not an IntoIterator implementation, so that the decision to handle errors one-by-one is explicit, by calling this method.

Source

pub fn peek(&self) -> &[E]

Inspect the list of errors, without considering them handled.

let errors = ErrorSentinel::new(vec!["error 1", "error 2"]);
assert_eq!(errors.peek(), &["error 1", "error 2"]);
errors.ignore(); // Without this, the sentinel would still panic
Source

pub fn len(&self) -> usize

The number of errors within this ErrorSentinel.

let errors = ErrorSentinel::new(vec!["error 1", "error 2"]);
assert_eq!(errors.len(), 2);
Source

pub fn any(&self) -> bool

Returns true if this ErrorSentinel has any errors.

Source

pub fn unwrap(self)
where E: Debug,

Handles the errors by 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 errors = ErrorSentinel::new(vec!["error 1", "error 2"]);
errors.unwrap(); // Panics
let errors = ErrorSentinel::ok();
errors.unwrap(); // OK
Source

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

Handles the errors by panicking with a message if there are any errors.

let errors = ErrorSentinel::new(vec!["error 1", "error 2"]);
errors.expect("something went wrong"); // Panics
let errors = ErrorSentinel::ok();
errors.expect("something went wrong"); // OK
Source§

impl ErrorSentinel<!>

Source

pub fn ok() -> Self

Constructs an ErrorSentinel which does not and will never contain errors, by using the never type ! as the error type.

Source

pub fn safely_ignore(self)

An alias for ignore which is only available when the error type is the never type !.

In this case, an error can never occur, so it is safe to ignore errors. Using safely_ignore instead of ignore will signal to readers that this is a safe assumption, and will cause a compile error if the error type ever changes from !.

let errors = ErrorSentinel::ok();
errors.safely_ignore(); // Prevents panic

Trait Implementations§

Source§

impl<E> Drop for ErrorSentinel<E>

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

impl<E> ErrorCollector<E> for ErrorSentinel<E>

Source§

type WrappedInner = ()

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.

Auto Trait Implementations§

§

impl<E> Freeze for ErrorSentinel<E>

§

impl<E> RefUnwindSafe for ErrorSentinel<E>
where E: RefUnwindSafe,

§

impl<E> Send for ErrorSentinel<E>
where E: Send,

§

impl<E> Sync for ErrorSentinel<E>
where E: Sync,

§

impl<E> Unpin for ErrorSentinel<E>
where E: Unpin,

§

impl<E> UnwindSafe for ErrorSentinel<E>
where 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> 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, 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.