Continue execution even if the Result is Err and return an error later


#1

I have code of the following structure

fn run() -> Result<(), failure::Error> {
    do_first_thing()?;
    do_second_thing()?;
    save_the_details()?;
    send_an_email()?;
    Ok(())
}

Actually, the save_the_details and send_an_email actions do not depend on each other - i.e. even if save_the_details fails I want to continue and send_an_email anyway. If only save_the_details failed, I want to the function to return an error, even if send_an_email succeeded. If both of them fail, I want to return an error indicating that both of them failed.

What’s the idiomatic way of handling this? Unfortunately, neither Vec<Result<T,E>> nor Result<T, Vec<E>> do not implement Fail so they cannot be used with failure. (and using ? would fail anyway due to type mismatch)


#2

Is this an error that you’ll be pattern-matching on in the caller or just informational? If it’s important for the caller to know that both pieces failed, then you should define your own error type (that derives Fail) where you capture this fact.


#3

I’d probably reach for something like this:

fn run() -> Result<(), failure::Error> {
    do_first_thing()?;
    do_second_thing()?;
    let r1 = save_the_details();
    let r2 = send_an_email();
    match (r1, r2) {
        (Err(e1), Err(e2)) => { ... do something with both errors ... }
        _ => { r1?; r2?; }
    }
    Ok(())
}

If you’re used to languages like Java, you can think of "? later" as finally. So

try { foo() } finally { bar() } // pseudo-Java

Translates into something like

let r = foo(); bar()?; r?

#4

I’d create a new AggregateFailure type to represent a collection of errors that just contains a Vec<Error>.

Another approach is to create some sort of Diagnostic type which gets passed into your function as &mut Diagnostic. Then whenever there’s an error the functions will call some add_error() method to add an error to the diagnostic struct. This is essentially what rustc does to aggregate the errors encountered during a pass and then present them all at the end (instead of stopping at the first error like normal ?).


#5

This doesn’t seem to scale, what if there are n actions which can fail but we want to execute all of them and return an aggregate error.


#6

… and implement Fail for AggregateFailure and then return it as a usual error, that’s what you mean?


#7

Hm, now that I think about it, I recall frunk has a feature whose use case is, as far as I can tell, pretty much exactly this. You build a result-like type that holds an HList (basically a variadic tuple) for success, or a Vec of all errors.


#8

The “aggregate error” is the hard part here, since giving that a type is awkward. It could be (Option<E1>, Option<E2>, ...), but that’s not quite right since (None, None, ...) wouldn’t actually be possible. Is there a big reason that all the errors are needed, and not just one of the errors?


#9

Any reason the errors can’t be contained in a Vec<Box<Error>>? Do they need to be inspected by type somewhere? It’s still unclear what the error handling requirements are.