[duplicate] Useability aids for `std::error::Error`

Since std::error::Error gained the source method (and description/cause were deprecated), there has been much less need for other error types like failure::Fail. However, libraries like failure also included useful helper methods for dealing with errors, especially around chaining errors together.

What do others think about the maturity of these helper methods, and specifically whether any of them are ready for inclusion into the standard library?

For me personally, one that I think would be useful is iter_sources functionality. It saves some fiddly code around storing internal pointers to the error struct. To show how it would look, I implemented it as an extension trait below.

pub trait ErrorExt<'a> {
    type Iter: Iterator<Item = &'a (dyn StdError + 'static)>;

    fn iter_sources(&self) -> Self::Iter;

    fn print_chain(&self);
}

impl<'a> ErrorExt<'a> for &'a (dyn StdError + 'static) {
    type Iter = Iter<'a>;
    /// Iterate through the sources of this error.
    ///
    /// The iterator does *not* include the top-level error.
    fn iter_sources(&self) -> Self::Iter {
        Iter(self.source())
    }

    fn print_chain(&self) {
        eprintln!("error: {}", self);
        for source in self.iter_sources() {
            eprintln!("caused by: {}", source);
        }
    }
}

/// I can't get this to work without the reference, because I don't know 
/// how to tie the lifetime of the iterator to the lifetime of the box.
impl<'a> ErrorExt<'a> for &'a Box<dyn std::error::Error + 'static> {
    type Iter = Iter<'a>;
    /// Iterate through the sources of this error.
    ///
    /// The iterator does *not* include the top-level error.
    fn iter_sources(&self) -> Self::Iter {
        Iter(self.source())
    }

    fn print_chain(&self) {
        eprintln!("error: {}", self);
        for source in self.iter_sources() {
            eprintln!("caused by: {}", source);
        }
    }
}

pub struct Iter<'a>(Option<&'a (dyn StdError + 'static)>);

impl<'a> Iterator for Iter<'a> {
    type Item = &'a (dyn StdError + 'static);

    fn next(&mut self) -> Option<Self::Item> {
        let current = self.0;
        if let Some(inner) = current {
            self.0 = inner.source();
        }
        current
    }
}

Iā€™m interested to hear the thoughts of others. Deciding whether something is stable enough to include in std can be contentious, but I feel it is important to have proposals like this from time to time, to see if functionality has reached a steady-state best-practice.

https://doc.rust-lang.org/std/error/trait.Error.html#method.iter_chain

1 Like

Urgh missed that :upside_down_face: