about summary refs log tree commit diff
path: root/src/libstd/error.rs
diff options
context:
space:
mode:
authorHarald Hoyer <harald@redhat.com>2019-02-07 16:10:34 +0100
committerHarald Hoyer <harald@hoyer.xyz>2019-02-09 16:04:25 +0100
commitf06af1ff178014dadd62391a4a06e7fff8f2a6a1 (patch)
tree9588f5e1fda722fdf9f4ef40f28cc3c4265e0881 /src/libstd/error.rs
parent825f355c7483746f3a17166f34dfabe3b2df1741 (diff)
downloadrust-f06af1ff178014dadd62391a4a06e7fff8f2a6a1.tar.gz
rust-f06af1ff178014dadd62391a4a06e7fff8f2a6a1.zip
impl iter_sources() and iter_chain() for dyn Error
Examples:

```rust
let next_error_type_a = err
    .iter_chain()
    .filter_map(Error::downcast_ref::<ErrorTypeA>)
    .next();
```

```rust
let source_root_error = err.iter_chain().last();
```

Credit for the ErrorIter goes to Tim Diekmann
https://www.reddit.com/r/rust/comments/aj3lpg/is_an_iterator_impl_over_errorsource_possible/
Diffstat (limited to 'src/libstd/error.rs')
-rw-r--r--src/libstd/error.rs152
1 files changed, 152 insertions, 0 deletions
diff --git a/src/libstd/error.rs b/src/libstd/error.rs
index 50415d9aeb9..de27333d28c 100644
--- a/src/libstd/error.rs
+++ b/src/libstd/error.rs
@@ -667,6 +667,158 @@ impl dyn Error {
             Err(self)
         }
     }
+
+    /// Returns an iterator starting with the current error and continuing with
+    /// recursively calling [`source`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(error_iter)]
+    /// use std::error::Error;
+    /// use std::fmt;
+    ///
+    /// #[derive(Debug)]
+    /// struct A;
+    ///
+    /// #[derive(Debug)]
+    /// struct B(Option<Box<dyn Error + 'static>>);
+    ///
+    /// impl fmt::Display for A {
+    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    ///         write!(f, "A")
+    ///     }
+    /// }
+    ///
+    /// impl fmt::Display for B {
+    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    ///         write!(f, "B")
+    ///     }
+    /// }
+    ///
+    /// impl Error for A {}
+    ///
+    /// impl Error for B {
+    ///     fn source(&self) -> Option<&(dyn Error + 'static)> {
+    ///         self.0.as_ref().map(|e| e.as_ref())
+    ///     }
+    /// }
+    ///
+    /// let b = B(Some(Box::new(A)));
+    ///
+    /// // let err : Box<Error> = b.into(); // or
+    /// let err = &b as &(dyn Error);
+    ///
+    /// let mut iter = err.iter_chain();
+    ///
+    /// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
+    /// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
+    /// assert!(iter.next().is_none());
+    /// assert!(iter.next().is_none());
+    /// ```
+    ///
+    /// [`source`]: trait.Error.html#method.source
+    #[unstable(feature = "error_iter", issue = "58289")]
+    #[inline]
+    pub fn iter_chain(&self) -> ErrorIter {
+        ErrorIter {
+            current: Some(self),
+        }
+    }
+
+    /// Returns an iterator starting with the [`source`] of this error
+    /// and continuing with recursively calling [`source`].
+    ///
+    /// # Examples
+    ///
+    /// ```
+    /// #![feature(error_iter)]
+    /// use std::error::Error;
+    /// use std::fmt;
+    ///
+    /// #[derive(Debug)]
+    /// struct A;
+    ///
+    /// #[derive(Debug)]
+    /// struct B(Option<Box<dyn Error + 'static>>);
+    ///
+    /// #[derive(Debug)]
+    /// struct C(Option<Box<dyn Error + 'static>>);
+    ///
+    /// impl fmt::Display for A {
+    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    ///         write!(f, "A")
+    ///     }
+    /// }
+    ///
+    /// impl fmt::Display for B {
+    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    ///         write!(f, "B")
+    ///     }
+    /// }
+    ///
+    /// impl fmt::Display for C {
+    ///     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
+    ///         write!(f, "C")
+    ///     }
+    /// }
+    ///
+    /// impl Error for A {}
+    ///
+    /// impl Error for B {
+    ///     fn source(&self) -> Option<&(dyn Error + 'static)> {
+    ///         self.0.as_ref().map(|e| e.as_ref())
+    ///     }
+    /// }
+    ///
+    /// impl Error for C {
+    ///     fn source(&self) -> Option<&(dyn Error + 'static)> {
+    ///         self.0.as_ref().map(|e| e.as_ref())
+    ///     }
+    /// }
+    ///
+    /// let b = B(Some(Box::new(A)));
+    /// let c = C(Some(Box::new(b)));
+    ///
+    /// // let err : Box<Error> = c.into(); // or
+    /// let err = &c as &(dyn Error);
+    ///
+    /// let mut iter = err.iter_sources();
+    ///
+    /// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
+    /// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
+    /// assert!(iter.next().is_none());
+    /// assert!(iter.next().is_none());
+    /// ```
+    ///
+    /// [`source`]: trait.Error.html#method.source
+    #[inline]
+    #[unstable(feature = "error_iter", issue = "58289")]
+    pub fn iter_sources(&self) -> ErrorIter {
+        ErrorIter {
+            current: self.source(),
+        }
+    }
+}
+
+/// An iterator over [`Error`]
+///
+/// [`Error`]: trait.Error.html
+#[unstable(feature = "error_iter", issue = "58289")]
+#[derive(Copy, Clone, Debug)]
+pub struct ErrorIter<'a> {
+    current: Option<&'a (dyn Error + 'static)>,
+}
+
+#[unstable(feature = "error_iter", issue = "58289")]
+impl<'a> Iterator for ErrorIter<'a> {
+    type Item = &'a (dyn Error + 'static);
+
+    fn next(&mut self) -> Option<Self::Item> {
+        let current = self.current;
+        self.current = self.current.and_then(Error::source);
+        current
+    }
 }
 
 impl dyn Error + Send {