Returning stack of errors in Result<T, Vec<E>>

pub fn bar() -> Result<T, Vec<E>> { ... }
pub fn bar2() -> Result<T2, Vec<E>> { .... }

pub fn some_func() -> Result<T, Vec<E>> {
  let e = E::new(...);
  {
    anytime here, when we use ? on something that returns a Result<T, Vec<E>>,
    I would like to do a Vec::push(_, e) to the end of the error
  }
}

It is possible to achieve the above with a 'wrapper' function. Is there something more elegant ?

pub fn _some_func() -> Result<T, Vec<E>> {
  ...
}

pub fn some_func() -> Result<T, Vec<E>> {
  let e = E::new(...);
  match _some_funct() {
    Ok(t) => Ok(t)
    Err(v) => { v.push(e); Err(v) }
  }
}

This pattern is implemented by @fosskers' validated crate:

https://docs.rs/validated/0.1.1/validated/

3 Likes
pub fn some_func() -> Result<T, Vec<E>> {
    let e = E::new(...);
    let save_err = |mut v: Vec<E>| { v.push(e.clone()); v };
    {
        let _t2 = bar2().map_err(save_err)?;
        let tee = bar().map_err(save_err)?;
        Ok(tee)
    }
}

Playground

Edit: Probably you want to just E::new(...) in the closure.

2 Likes

Does this break ? ? I was under the impression that ? only worked on functions that returns Option or Result.

I ended up going with a macro that wraps the map_err.

Why don't you make the error type itself wraps prev error? And use Error::source() for backtrace.

Maybe a vec of errors is the correct semantics, for example they might be copying a list of files and want the status of each file after the copy, in which case it makes sense to have a vector of the errors that could happen. You're correct that the vector itself could be wrapped in another error depending on what OP is doing

Since OP mentioned "stack of errors" and Err(v) => { v.push(e); Err(v) } wrapped in a macro.

1 Like

Currently using std::file! and std::line!

Can you expand on this? What exactly are you suggesting? I'm not very detail oriented when it comes to errors; I tend to have on Err enum per crate, and the main goal of this Vec is so keep a 'stack' of what actions (from general to narrow) prog was trying to perform when it ran into error.

When you ? the Err::<_, E1>(e1) within the function returning Result<_, E2>, the value e1 is converted to E2 type using From impl. The common impl is to wrap the e1 within the E2 with some additional data and/or variants. thiserror crate makes it really easier with its #[from] attribute. Another case of this pattern is anyhow::Context which allows to wrap the given error with some contexture message.

This depends on E1 and E2 being different types right? So I would need to have very fine grained errors and a 'hierarchy' of error types. Is that correct ?

Yeah, if they're same such automatic conversion is not possible. You may need to be a bit more explicit like the .context("some reason")?.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.