Skip to content

Commit f06af1f

Browse files
committed
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/
1 parent 825f355 commit f06af1f

File tree

1 file changed

+152
-0
lines changed

1 file changed

+152
-0
lines changed

src/libstd/error.rs

+152
Original file line numberDiff line numberDiff line change
@@ -667,6 +667,158 @@ impl dyn Error {
667667
Err(self)
668668
}
669669
}
670+
671+
/// Returns an iterator starting with the current error and continuing with
672+
/// recursively calling [`source`].
673+
///
674+
/// # Examples
675+
///
676+
/// ```
677+
/// #![feature(error_iter)]
678+
/// use std::error::Error;
679+
/// use std::fmt;
680+
///
681+
/// #[derive(Debug)]
682+
/// struct A;
683+
///
684+
/// #[derive(Debug)]
685+
/// struct B(Option<Box<dyn Error + 'static>>);
686+
///
687+
/// impl fmt::Display for A {
688+
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
689+
/// write!(f, "A")
690+
/// }
691+
/// }
692+
///
693+
/// impl fmt::Display for B {
694+
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
695+
/// write!(f, "B")
696+
/// }
697+
/// }
698+
///
699+
/// impl Error for A {}
700+
///
701+
/// impl Error for B {
702+
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
703+
/// self.0.as_ref().map(|e| e.as_ref())
704+
/// }
705+
/// }
706+
///
707+
/// let b = B(Some(Box::new(A)));
708+
///
709+
/// // let err : Box<Error> = b.into(); // or
710+
/// let err = &b as &(dyn Error);
711+
///
712+
/// let mut iter = err.iter_chain();
713+
///
714+
/// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
715+
/// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
716+
/// assert!(iter.next().is_none());
717+
/// assert!(iter.next().is_none());
718+
/// ```
719+
///
720+
/// [`source`]: trait.Error.html#method.source
721+
#[unstable(feature = "error_iter", issue = "58289")]
722+
#[inline]
723+
pub fn iter_chain(&self) -> ErrorIter {
724+
ErrorIter {
725+
current: Some(self),
726+
}
727+
}
728+
729+
/// Returns an iterator starting with the [`source`] of this error
730+
/// and continuing with recursively calling [`source`].
731+
///
732+
/// # Examples
733+
///
734+
/// ```
735+
/// #![feature(error_iter)]
736+
/// use std::error::Error;
737+
/// use std::fmt;
738+
///
739+
/// #[derive(Debug)]
740+
/// struct A;
741+
///
742+
/// #[derive(Debug)]
743+
/// struct B(Option<Box<dyn Error + 'static>>);
744+
///
745+
/// #[derive(Debug)]
746+
/// struct C(Option<Box<dyn Error + 'static>>);
747+
///
748+
/// impl fmt::Display for A {
749+
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
750+
/// write!(f, "A")
751+
/// }
752+
/// }
753+
///
754+
/// impl fmt::Display for B {
755+
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
756+
/// write!(f, "B")
757+
/// }
758+
/// }
759+
///
760+
/// impl fmt::Display for C {
761+
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
762+
/// write!(f, "C")
763+
/// }
764+
/// }
765+
///
766+
/// impl Error for A {}
767+
///
768+
/// impl Error for B {
769+
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
770+
/// self.0.as_ref().map(|e| e.as_ref())
771+
/// }
772+
/// }
773+
///
774+
/// impl Error for C {
775+
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
776+
/// self.0.as_ref().map(|e| e.as_ref())
777+
/// }
778+
/// }
779+
///
780+
/// let b = B(Some(Box::new(A)));
781+
/// let c = C(Some(Box::new(b)));
782+
///
783+
/// // let err : Box<Error> = c.into(); // or
784+
/// let err = &c as &(dyn Error);
785+
///
786+
/// let mut iter = err.iter_sources();
787+
///
788+
/// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
789+
/// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
790+
/// assert!(iter.next().is_none());
791+
/// assert!(iter.next().is_none());
792+
/// ```
793+
///
794+
/// [`source`]: trait.Error.html#method.source
795+
#[inline]
796+
#[unstable(feature = "error_iter", issue = "58289")]
797+
pub fn iter_sources(&self) -> ErrorIter {
798+
ErrorIter {
799+
current: self.source(),
800+
}
801+
}
802+
}
803+
804+
/// An iterator over [`Error`]
805+
///
806+
/// [`Error`]: trait.Error.html
807+
#[unstable(feature = "error_iter", issue = "58289")]
808+
#[derive(Copy, Clone, Debug)]
809+
pub struct ErrorIter<'a> {
810+
current: Option<&'a (dyn Error + 'static)>,
811+
}
812+
813+
#[unstable(feature = "error_iter", issue = "58289")]
814+
impl<'a> Iterator for ErrorIter<'a> {
815+
type Item = &'a (dyn Error + 'static);
816+
817+
fn next(&mut self) -> Option<Self::Item> {
818+
let current = self.current;
819+
self.current = self.current.and_then(Error::source);
820+
current
821+
}
670822
}
671823

672824
impl dyn Error + Send {

0 commit comments

Comments
 (0)