Skip to content

Commit e2df4da

Browse files
committed
std::error::Error: change the error iterator producer
To produce an error iterator `std::error::Chain` one had to call `<dyn Error>::chain()`, which was not very ergonomic, because you have to manually cast to a trait object, if you didn't already have one with the type erased. ``` let mut iter = (&my_error as &(dyn Error)).chain(); // or let mut iter = <dyn Error>::chain(&my_error); // or let mut iter = Error::chain(&my_error); ``` The `chain()` method can't be implemented on the Error trait, because of rust-lang#69161 `Chain::new()` replaces `<dyn Error>::chain()` as a good alternative without confusing users, why they can't use `my_error.chain()` directly. The `Error::sources()` method doesn't have this problem, so implement it more efficiently, than one could achieve this with `Chain::new().skip(1)`. Related: rust-lang#58520
1 parent b593389 commit e2df4da

File tree

1 file changed

+67
-37
lines changed

1 file changed

+67
-37
lines changed

library/std/src/error.rs

+67-37
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,49 @@ pub trait Error: Debug + Display {
9999
None
100100
}
101101

102+
/// Returns an iterator starting with the `source()` of this error
103+
/// and continuing with recursively calling `source()`
104+
///
105+
/// If you want to include the current error, use [`Chain::new`].
106+
///
107+
/// # Examples
108+
///
109+
/// ```
110+
/// #![feature(error_iter)]
111+
/// use std::error::Error;
112+
/// use std::fmt;
113+
///
114+
/// #[derive(Debug)]
115+
/// struct SourceError(&'static str, Option<Box<dyn Error + 'static>>);
116+
///
117+
/// impl fmt::Display for SourceError {
118+
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
119+
/// write!(f, "{}", self.0)
120+
/// }
121+
/// }
122+
///
123+
/// impl Error for SourceError {
124+
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
125+
/// self.1.as_ref().map(|e| e.as_ref())
126+
/// }
127+
/// }
128+
///
129+
/// let a = SourceError("A", None);
130+
/// let b = SourceError("B", Some(Box::new(a)));
131+
/// let c = SourceError("C", Some(Box::new(b)));
132+
///
133+
/// let mut iter = c.sources();
134+
///
135+
/// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
136+
/// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
137+
/// assert!(iter.next().is_none());
138+
/// ```
139+
#[unstable(feature = "error_iter", issue = "58520")]
140+
#[inline]
141+
fn sources(&self) -> Chain<'_> {
142+
Chain { current: self.source() }
143+
}
144+
102145
/// Gets the `TypeId` of `self`.
103146
#[doc(hidden)]
104147
#[unstable(
@@ -651,75 +694,62 @@ impl dyn Error {
651694
Err(self)
652695
}
653696
}
697+
}
698+
699+
/// An iterator over an [`Error`] and its sources.
700+
#[unstable(feature = "error_iter", issue = "58520")]
701+
#[derive(Clone, Debug)]
702+
pub struct Chain<'a> {
703+
current: Option<&'a (dyn Error + 'static)>,
704+
}
654705

706+
impl<'a> Chain<'a> {
655707
/// Returns an iterator starting with the current error and continuing with
656708
/// recursively calling [`Error::source`].
657709
///
658710
/// If you want to omit the current error and only use its sources,
659-
/// use `skip(1)`.
711+
/// use [`Error::sources`].
660712
///
661713
/// # Examples
662714
///
663715
/// ```
664716
/// #![feature(error_iter)]
665-
/// use std::error::Error;
717+
/// use std::error::{Error, Chain};
666718
/// use std::fmt;
667719
///
668720
/// #[derive(Debug)]
669-
/// struct A;
670-
///
671-
/// #[derive(Debug)]
672-
/// struct B(Option<Box<dyn Error + 'static>>);
721+
/// struct SourceError(&'static str, Option<Box<dyn Error + 'static>>);
673722
///
674-
/// impl fmt::Display for A {
675-
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
676-
/// write!(f, "A")
723+
/// impl fmt::Display for SourceError {
724+
/// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
725+
/// write!(f, "{}", self.0)
677726
/// }
678727
/// }
679728
///
680-
/// impl fmt::Display for B {
681-
/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
682-
/// write!(f, "B")
683-
/// }
684-
/// }
685-
///
686-
/// impl Error for A {}
687-
///
688-
/// impl Error for B {
729+
/// impl Error for SourceError {
689730
/// fn source(&self) -> Option<&(dyn Error + 'static)> {
690-
/// self.0.as_ref().map(|e| e.as_ref())
731+
/// self.1.as_ref().map(|e| e.as_ref())
691732
/// }
692733
/// }
693734
///
694-
/// let b = B(Some(Box::new(A)));
695-
///
696-
/// // let err : Box<Error> = b.into(); // or
697-
/// let err = &b as &(dyn Error);
735+
/// let a = SourceError("A", None);
736+
/// let b = SourceError("B", Some(Box::new(a)));
737+
/// let c = SourceError("C", Some(Box::new(b)));
698738
///
699-
/// let mut iter = err.chain();
739+
/// let mut iter = Chain::new(&c);
700740
///
741+
/// assert_eq!("C".to_string(), iter.next().unwrap().to_string());
701742
/// assert_eq!("B".to_string(), iter.next().unwrap().to_string());
702743
/// assert_eq!("A".to_string(), iter.next().unwrap().to_string());
703744
/// assert!(iter.next().is_none());
704-
/// assert!(iter.next().is_none());
705745
/// ```
706746
#[unstable(feature = "error_iter", issue = "58520")]
707747
#[inline]
708-
pub fn chain(&self) -> Chain<'_> {
709-
Chain { current: Some(self) }
748+
pub fn new(err: &'a (dyn Error + 'static)) -> Chain<'a> {
749+
Chain { current: Some(err) }
710750
}
711751
}
712752

713-
/// An iterator over an [`Error`] and its sources.
714-
///
715-
/// If you want to omit the initial error and only process
716-
/// its sources, use `skip(1)`.
717-
#[unstable(feature = "error_iter", issue = "58520")]
718-
#[derive(Clone, Debug)]
719-
pub struct Chain<'a> {
720-
current: Option<&'a (dyn Error + 'static)>,
721-
}
722-
723753
#[unstable(feature = "error_iter", issue = "58520")]
724754
impl<'a> Iterator for Chain<'a> {
725755
type Item = &'a (dyn Error + 'static);

0 commit comments

Comments
 (0)