Simple String Errors
The most simplest of doing error handling in rust is by returning String
as a Box<Error>
.
As you can see by running the example, this only prints out the last Error
.
If the rust main
function returns an Err(), this Err() will be displayed with std::fmt::Debug
.
use std::error::Error; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err("do_some_io error")?; Ok(()) } fn func2() -> Result<(), Box<Error>> { if let Err(_) = do_some_io() { Err("func2 error")?; } Ok(()) } fn func1() -> Result<(), Box<Error>> { if let Err(_) = func2() { Err("func1 error")?; } Ok(()) } fn main() -> Result<(), Box<Error>> { func1() }
Simple Chained String Errors
Now with the help of the chainerror
crate, we can have a nicer output.
Press the play button in the upper right corner and see the nice debug output.
use crate::chainerror::*; use std::error::Error; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err("do_some_io error")?; Ok(()) } fn func2() -> Result<(), Box<Error>> { if let Err(e) = do_some_io() { Err(cherr!(e, "func2 error"))?; } Ok(()) } fn func1() -> Result<(), Box<Error>> { if let Err(e) = func2() { Err(cherr!(e, "func1 error"))?; } Ok(()) } fn main() -> Result<(), Box<Error>> { func1() } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
What did we do here?
if let Err(e) = do_some_io() {
Err(cherr!(e, "func2 error"))?;
}
The macro cherr!(cause, newerror)
stores cause
as the source/cause of newerror
and returns
newerror
, along with the filename (file!()
) and line number (line!()
).
Err(e)?
then returns the error e
applying e.into()
, so that we
again have a Err(Box<Error>)
as a result.
The Debug
implementation of ChainError<T>
(which is returned by cherr!()
)
prints the Debug
of T
prefixed with the stored filename and line number.
ChainError<T>
is in our case ChainError<String>
.
Mapping Errors
Now let's get more rust idiomatic by using .map_err()
.
use crate::chainerror::*; use std::error::Error; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err("do_some_io error")?; Ok(()) } fn func2() -> Result<(), Box<Error>> { do_some_io().map_err(|e| cherr!(e, "func2 error"))?; Ok(()) } fn func1() -> Result<(), Box<Error>> { func2().map_err(|e| cherr!(e, "func1 error"))?; Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { eprintln!("{:?}", e); } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
If you compare the output to the previous example, you will see, that:
Error: src/main.rs:19: "func1 error"
changed to just:
src/main.rs:16: "func1 error"
This is, because we caught the error of func1()
in main()
and print it out ourselves.
We can now control, whether to output in Debug
or Display
mode.
Maybe depending on --debug
as a CLI argument.
Saving coding chars
Because decorating an error with more information should not
let you jump through hoops, chainerror
has a quick macro for that.
mstrerror!()
fits right into .map_err()
letting you quickly add
more debug strings.
mstrerror!()
even understands format!()
syntax like println!()
.
use crate::chainerror::*; use std::error::Error; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err("do_some_io error")?; Ok(()) } fn func2() -> Result<(), Box<Error>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box<Error>> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { eprintln!("{:?}", e); } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
The source() of Errors
Sometimes you want to inspect the source()
of an Error
.
chainerror
implements std::error::Error::source()
, so you can get the cause of an error.
use crate::chainerror::*; use std::error::Error; use std::io; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } fn func2() -> Result<(), Box<Error>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box<Error>> { if let Err(e) = func2() { if let Some(s) = e.source() { eprintln!("func2 failed because of '{}'", s); Err(e).map_err(mstrerr!("func1 error"))?; } } Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { eprintln!("{}", e); } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
Note, that we changed the output of the error in main()
from Debug
to Display
, so we don't see
the error backtrace with filename and line number.
To enable the Display
backtrace, you have to enable the feature display-cause
for chainerror
.
Downcast the Errors
[TBD]
use crate::chainerror::*; use std::error::Error; use std::io; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } fn func2() -> Result<(), Box<Error>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box<Error>> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { eprintln!("Error: {}", e); let mut s = e.as_ref(); while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::<io::Error>() { eprintln!("caused by: std::io::Error: {}", ioerror); match ioerror.kind() { io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"), _ => {} } } else { eprintln!("caused by: {}", c); } s = c; } } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
The root cause of all Errors
[TBD]
use crate::chainerror::*; use std::error::Error; use std::io; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } fn func2() -> Result<(), Box<Error>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!("Error reading '{}'", filename))?; Ok(()) } fn func1() -> Result<(), Box<Error>> { func2().map_err(mstrerr!("func1 error"))?; Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { eprintln!("Error: {}", e); if let Some(s) = e.downcast_chain_ref::<String>() { if let Some(ioerror) = s.find_cause::<io::Error>() { eprintln!("caused by: std::io::Error: {}", ioerror); match ioerror.kind() { io::ErrorKind::NotFound => eprintln!("of kind: std::io::ErrorKind::NotFound"), _ => {} } } if let Some(e) = s.root_cause() { let ioerror = e.downcast_ref::<io::Error>().unwrap(); eprintln!("The root cause was: std::io::Error: {:#?}", ioerror); } } } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
Finding an Error cause
[TBD]
use crate::chainerror::*; use std::error::Error; use std::io; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box<Error>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) } derive_str_cherr!(Func1Error); fn func1() -> Result<(), Box<Error>> { func2().map_err(mstrerr!(Func1Error, "func1 error"))?; Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { if let Some(f1err) = e.downcast_chain_ref::<Func1Error>() { eprintln!("Func1Error: {}", f1err); if let Some(f2err) = f1err.find_cause::<ChainError<Func2Error>>() { eprintln!("Func2Error: {}", f2err); } if let Some(f2err) = f1err.find_chain_cause::<Func2Error>() { eprintln!("Debug Func2Error:\n{:?}", f2err); } } } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
Selective Error Handling
[TBD]
use crate::chainerror::*; use std::error::Error; use std::io; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box<Error>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) } derive_str_cherr!(Func1ErrorFunc2); derive_str_cherr!(Func1ErrorIO); fn func1() -> Result<(), Box<Error>> { func2().map_err(mstrerr!(Func1ErrorFunc2, "func1 error calling func2"))?; let filename = "bar.txt"; do_some_io().map_err(mstrerr!(Func1ErrorIO, "Error reading '{}'", filename))?; Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { if let Some(s) = e.downcast_ref::<ChainError<Func1ErrorIO>>() { eprintln!("Func1ErrorIO:\n{:?}", s); } if let Some(s) = try_cherr_ref!(e, Func1ErrorFunc2) { eprintln!("Func1ErrorFunc2:\n{:?}", s); } } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
ErrorKind to the rescue
[TBD]
use crate::chainerror::*; use std::error::Error; use std::io; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box<Error>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) } #[derive(Debug)] enum Func1Error { Func2, IO(String), } impl ::std::fmt::Display for Func1Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match self { Func1Error::Func2 => write!(f, "func1 error calling func2"), Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename), } } } fn func1() -> ChainResult<(), Func1Error> { func2().map_err(|e| cherr!(e, Func1Error::Func2))?; let filename = String::from("bar.txt"); do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { match e.kind() { Func1Error::Func2 => eprintln!("Main Error Report: func1 error calling func2"), Func1Error::IO(filename) => { eprintln!("Main Error Report: func1 error reading '{}'", filename) } } if let Some(e) = e.find_chain_cause::<Func2Error>() { eprintln!("Error reported by Func2Error: {}", e) } eprintln!("\nDebug Error:\n{:?}", e); } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }
Debug for the ErrorKind
[TBD]
use crate::chainerror::*; use std::error::Error; use std::io; use std::result::Result; fn do_some_io() -> Result<(), Box<Error>> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(()) } derive_str_cherr!(Func2Error); fn func2() -> Result<(), Box<Error>> { let filename = "foo.txt"; do_some_io().map_err(mstrerr!(Func2Error, "Error reading '{}'", filename))?; Ok(()) } enum Func1Error { Func2, IO(String), } impl ::std::fmt::Display for Func1Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { match self { Func1Error::Func2 => write!(f, "func1 error calling func2"), Func1Error::IO(filename) => write!(f, "Error reading '{}'", filename), } } } impl ::std::fmt::Debug for Func1Error { fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { write!(f, "{}", self) } } fn func1() -> ChainResult<(), Func1Error> { func2().map_err(|e| cherr!(e, Func1Error::Func2))?; let filename = String::from("bar.txt"); do_some_io().map_err(|e| cherr!(e, Func1Error::IO(filename)))?; Ok(()) } fn main() -> Result<(), Box<Error>> { if let Err(e) = func1() { match e.kind() { Func1Error::Func2 => eprintln!("Main Error Report: func1 error calling func2"), Func1Error::IO(filename) => { eprintln!("Main Error Report: func1 error reading '{}'", filename) } } if let Some(e) = e.find_chain_cause::<Func2Error>() { eprintln!("Error reported by Func2Error: {}", e) } eprintln!("\nDebug Error:\n{:?}", e); } Ok(()) } # #[allow(dead_code)] # mod chainerror { # use std::error::Error; # use std::fmt::{Debug, Display, Formatter, Result}; # use std::result::Result as StdResult; # # pub struct ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # occurrence: Option<(u32, &'static str)>, # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # } # # pub type ChainResult<O, E> = StdResult<O, ChainError<E>>; # # impl<T: 'static + Display + Debug> ChainError<T> { # #[cfg(not(feature = "no-fileline"))] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { # occurrence, # kind, # error_cause, # } # } # # #[cfg(feature = "no-fileline")] # pub fn new( # kind: T, # error_cause: Option<Box<dyn Error + 'static>>, # _occurrence: Option<(u32, &'static str)>, # ) -> Self { # Self { kind, error_cause } # } # # pub fn root_cause(&self) -> Option<&(dyn Error + 'static)> { # let mut cause = self as &(dyn Error + 'static); # while let Some(c) = cause.source() { # cause = c; # } # Some(cause) # } # # pub fn find_cause<U: Error + 'static>(&self) -> Option<&U> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<U>() { # return cause.downcast_ref::<U>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn find_chain_cause<U: Error + 'static>(&self) -> Option<&ChainError<U>> { # let mut cause = self as &(dyn Error + 'static); # loop { # if cause.is::<ChainError<U>>() { # return cause.downcast_ref::<ChainError<U>>(); # } # # match cause.source() { # Some(c) => cause = c, # None => return None, # } # } # } # # pub fn kind<'a>(&'a self) -> &'a T { # &self.kind # } # } # # pub trait ChainErrorDown { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool; # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>>; # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>>; # } # # use std::any::TypeId; # # impl<U: 'static + Display + Debug> ChainErrorDown for ChainError<U> { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # TypeId::of::<T>() == TypeId::of::<U>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&*(self as *const dyn Error as *const &ChainError<T>)) } # } else { # None # } # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # if self.is_chain::<T>() { # unsafe { Some(&mut *(self as *mut dyn Error as *mut &mut ChainError<T>)) } # } else { # None # } # } # } # # impl ChainErrorDown for dyn Error + 'static { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl ChainErrorDown for dyn Error + 'static + Send + Sync { # fn is_chain<T: 'static + Display + Debug>(&self) -> bool { # self.is::<ChainError<T>>() # } # # fn downcast_chain_ref<T: 'static + Display + Debug>(&self) -> Option<&ChainError<T>> { # self.downcast_ref::<ChainError<T>>() # } # # fn downcast_chain_mut<T: 'static + Display + Debug>(&mut self) -> Option<&mut ChainError<T>> { # self.downcast_mut::<ChainError<T>>() # } # } # # impl<T: 'static + Display + Debug> Error for ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Error for &mut ChainError<T> { # fn source(&self) -> Option<&(dyn Error + 'static)> { # if let Some(ref e) = self.error_cause { # Some(e.as_ref()) # } else { # None # } # } # } # # impl<T: 'static + Display + Debug> Display for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # write!(f, "{}", self.kind)?; # # #[cfg(feature = "display-cause")] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Display::fmt(&e, f)?; # } # } # Ok(()) # } # } # # impl<T: 'static + Display + Debug> Debug for ChainError<T> { # fn fmt(&self, f: &mut Formatter) -> Result { # #[cfg(not(feature = "no-fileline"))] # { # if let Some(o) = self.occurrence { # write!(f, "{}:{}: ", o.1, o.0)?; # } # } # # Debug::fmt(&self.kind, f)?; # # #[cfg(not(feature = "no-debug-cause"))] # { # if let Some(e) = self.source() { # writeln!(f, "\nCaused by:")?; # Debug::fmt(&e, f)?; # } # } # Ok(()) # } # } # # #[macro_export] # macro_rules! cherr { # ( $k:expr ) => { # ChainError::<_>::new($k, None, Some((line!(), file!()))) # }; # ( $e:expr, $k:expr ) => { # ChainError::<_>::new($k, Some(Box::from($e)), Some((line!(), file!()))) # }; # } # # #[macro_export] # macro_rules! mstrerr { # ( $t:ident, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $t:path, $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, $t (format!($v, $( $more , )* ))) # }; # ( $v:expr $(, $more:expr)* ) => { # |e| cherr!(e, format!($v, $( $more , )* )) # }; # } # # #[macro_export] # macro_rules! derive_str_cherr { # ($e:ident) => { # struct $e(String); # impl ::std::fmt::Display for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}", self.0) # } # } # impl ::std::fmt::Debug for $e { # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result { # write!(f, "{}({})", stringify!($e), self.0) # } # } # impl ::std::error::Error for $e {} # }; # } # # #[macro_export] # macro_rules! try_cherr_ref { # ( $e:expr, $t:ident ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_ref::<ChainError<$t>>() # }; # } # # #[macro_export] # macro_rules! try_cherr_mut { # ( $e:expr, $t:ident ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # ( $e:expr, $t:path ) => { # $e.downcast_mut::<ChainError<$t>>() # }; # } # }