From e2df4da9ec43d1c878a3be332720787372014aa0 Mon Sep 17 00:00:00 2001 From: Harald Hoyer Date: Wed, 3 Feb 2021 12:59:08 +0100 Subject: [PATCH] std::error::Error: change the error iterator producer To produce an error iterator `std::error::Chain` one had to call `::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 = ::chain(&my_error); // or let mut iter = Error::chain(&my_error); ``` The `chain()` method can't be implemented on the Error trait, because of https://github.com/rust-lang/rust/issues/69161 `Chain::new()` replaces `::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: https://github.com/rust-lang/rust/issues/58520 --- library/std/src/error.rs | 104 +++++++++++++++++++++++++-------------- 1 file changed, 67 insertions(+), 37 deletions(-) diff --git a/library/std/src/error.rs b/library/std/src/error.rs index 605d953f5da71..c7645dbd87039 100644 --- a/library/std/src/error.rs +++ b/library/std/src/error.rs @@ -99,6 +99,49 @@ pub trait Error: Debug + Display { None } + /// Returns an iterator starting with the `source()` of this error + /// and continuing with recursively calling `source()` + /// + /// If you want to include the current error, use [`Chain::new`]. + /// + /// # Examples + /// + /// ``` + /// #![feature(error_iter)] + /// use std::error::Error; + /// use std::fmt; + /// + /// #[derive(Debug)] + /// struct SourceError(&'static str, Option>); + /// + /// impl fmt::Display for SourceError { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.0) + /// } + /// } + /// + /// impl Error for SourceError { + /// fn source(&self) -> Option<&(dyn Error + 'static)> { + /// self.1.as_ref().map(|e| e.as_ref()) + /// } + /// } + /// + /// let a = SourceError("A", None); + /// let b = SourceError("B", Some(Box::new(a))); + /// let c = SourceError("C", Some(Box::new(b))); + /// + /// let mut iter = c.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()); + /// ``` + #[unstable(feature = "error_iter", issue = "58520")] + #[inline] + fn sources(&self) -> Chain<'_> { + Chain { current: self.source() } + } + /// Gets the `TypeId` of `self`. #[doc(hidden)] #[unstable( @@ -651,75 +694,62 @@ impl dyn Error { Err(self) } } +} + +/// An iterator over an [`Error`] and its sources. +#[unstable(feature = "error_iter", issue = "58520")] +#[derive(Clone, Debug)] +pub struct Chain<'a> { + current: Option<&'a (dyn Error + 'static)>, +} +impl<'a> Chain<'a> { /// Returns an iterator starting with the current error and continuing with /// recursively calling [`Error::source`]. /// /// If you want to omit the current error and only use its sources, - /// use `skip(1)`. + /// use [`Error::sources`]. /// /// # Examples /// /// ``` /// #![feature(error_iter)] - /// use std::error::Error; + /// use std::error::{Error, Chain}; /// use std::fmt; /// /// #[derive(Debug)] - /// struct A; - /// - /// #[derive(Debug)] - /// struct B(Option>); + /// struct SourceError(&'static str, Option>); /// - /// impl fmt::Display for A { - /// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - /// write!(f, "A") + /// impl fmt::Display for SourceError { + /// fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + /// write!(f, "{}", self.0) /// } /// } /// - /// 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 { + /// impl Error for SourceError { /// fn source(&self) -> Option<&(dyn Error + 'static)> { - /// self.0.as_ref().map(|e| e.as_ref()) + /// self.1.as_ref().map(|e| e.as_ref()) /// } /// } /// - /// let b = B(Some(Box::new(A))); - /// - /// // let err : Box = b.into(); // or - /// let err = &b as &(dyn Error); + /// let a = SourceError("A", None); + /// let b = SourceError("B", Some(Box::new(a))); + /// let c = SourceError("C", Some(Box::new(b))); /// - /// let mut iter = err.chain(); + /// let mut iter = Chain::new(&c); /// + /// assert_eq!("C".to_string(), iter.next().unwrap().to_string()); /// 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()); /// ``` #[unstable(feature = "error_iter", issue = "58520")] #[inline] - pub fn chain(&self) -> Chain<'_> { - Chain { current: Some(self) } + pub fn new(err: &'a (dyn Error + 'static)) -> Chain<'a> { + Chain { current: Some(err) } } } -/// An iterator over an [`Error`] and its sources. -/// -/// If you want to omit the initial error and only process -/// its sources, use `skip(1)`. -#[unstable(feature = "error_iter", issue = "58520")] -#[derive(Clone, Debug)] -pub struct Chain<'a> { - current: Option<&'a (dyn Error + 'static)>, -} - #[unstable(feature = "error_iter", issue = "58520")] impl<'a> Iterator for Chain<'a> { type Item = &'a (dyn Error + 'static);