{"doc_urls":["index.html#chainerror","index.html#multiple-output-formats","index.html#tutorial","index.html#license","index.html#contribution","tutorial1.html#simple-string-errors","tutorial2.html#simple-chained-string-errors","tutorial2.html#what-did-we-do-here","tutorial3.html#mapping-errors","tutorial4.html#more-information","tutorial5.html#the-source-of-errors","tutorial6.html#downcast-the-errors","tutorial7.html#the-root-cause-of-all-errors","tutorial8.html#finding-an-error-cause","tutorial9.html#selective-error-handling","tutorial10.html#errorkind-to-the-rescue","tutorial11.html#debug-for-the-errorkind","tutorial12.html#deref-for-the-errorkind","tutorial13.html#writing-a-library","tutorial14.html#going-back-to-std","end.html#the-end"],"index":{"documentStore":{"docInfo":{"0":{"body":196,"breadcrumbs":2,"title":1},"1":{"body":95,"breadcrumbs":4,"title":3},"10":{"body":1403,"breadcrumbs":4,"title":2},"11":{"body":1417,"breadcrumbs":4,"title":2},"12":{"body":1452,"breadcrumbs":6,"title":3},"13":{"body":1415,"breadcrumbs":6,"title":3},"14":{"body":1429,"breadcrumbs":6,"title":3},"15":{"body":1469,"breadcrumbs":4,"title":2},"16":{"body":1482,"breadcrumbs":4,"title":2},"17":{"body":1464,"breadcrumbs":4,"title":2},"18":{"body":1488,"breadcrumbs":4,"title":2},"19":{"body":350,"breadcrumbs":6,"title":3},"2":{"body":2,"breadcrumbs":2,"title":1},"20":{"body":18,"breadcrumbs":2,"title":1},"3":{"body":16,"breadcrumbs":2,"title":1},"4":{"body":21,"breadcrumbs":2,"title":1},"5":{"body":106,"breadcrumbs":6,"title":3},"6":{"body":1383,"breadcrumbs":8,"title":4},"7":{"body":40,"breadcrumbs":5,"title":1},"8":{"body":1403,"breadcrumbs":4,"title":2},"9":{"body":1375,"breadcrumbs":4,"title":2}},"docs":{"0":{"body":"Crate Rust Documentation Coverage Status Maintenance chainerror provides an error backtrace without doing a real backtrace, so even after you strip your binaries, you still have the error backtrace. Having nested function returning errors, the output doesn't tell where the error originates from. use std::path::PathBuf; type BoxedError = Box;\nfn read_config_file(path: PathBuf) -> Result<(), BoxedError> { // do stuff, return other errors let _buf = std::fs::read_to_string(&path)?; // do stuff, return other errors Ok(())\n} fn process_config_file() -> Result<(), BoxedError> { // do stuff, return other errors let _buf = read_config_file(\"foo.txt\".into())?; // do stuff, return other errors Ok(())\n} fn main() { if let Err(e) = process_config_file() { eprintln!(\"Error:\\n{:?}\", e); }\n} This gives the output: Error:\nOs { code: 2, kind: NotFound, message: \"No such file or directory\" } and you have no idea where it comes from. With chainerror, you can supply a context and get a nice error backtrace: use chainerror::Context as _;\nuse std::path::PathBuf; type BoxedError = Box;\nfn read_config_file(path: PathBuf) -> Result<(), BoxedError> { // do stuff, return other errors let _buf = std::fs::read_to_string(&path).context(format!(\"Reading file: {:?}\", &path))?; // do stuff, return other errors Ok(())\n} fn process_config_file() -> Result<(), BoxedError> { // do stuff, return other errors let _buf = read_config_file(\"foo.txt\".into()).context(\"read the config file\")?; // do stuff, return other errors Ok(())\n} fn main() { if let Err(e) = process_config_file() { eprintln!(\"Error:\\n{:?}\", e); }\n} with the output: Error:\nexamples/simple.rs:14:51: read the config file\nCaused by:\nexamples/simple.rs:7:47: Reading file: \"foo.txt\"\nCaused by:\nOs { code: 2, kind: NotFound, message: \"No such file or directory\" } chainerror uses .source() of std::error::Error along with #[track_caller] and Location to provide a nice debug error backtrace. It encapsulates all types, which have Display + Debug and can store the error cause internally. Along with the Error struct, chainerror comes with some useful helper macros to save a lot of typing. chainerror has no dependencies! Debug information is worth it!","breadcrumbs":"chainerror » chainerror","id":"0","title":"chainerror"},"1":{"body":"chainerror supports multiple output formats, which can be selected with the different format specifiers: {}: Display func1 error calling func2 {:#}: Alternative Display func1 error calling func2\nCaused by: func2 error: calling func3\nCaused by: (passed error)\nCaused by: Error reading 'foo.txt'\nCaused by: entity not found {:?}: Debug examples/example.rs:50:13: func1 error calling func2\nCaused by:\nexamples/example.rs:25:13: Func2Error(func2 error: calling func3)\nCaused by:\nexamples/example.rs:18:13: (passed error)\nCaused by:\nexamples/example.rs:13:18: Error reading 'foo.txt'\nCaused by:\nKind(NotFound) {:#?}: Alternative Debug Error { occurrence: Some( \"examples/example.rs:50:13\", ), kind: func1 error calling func2, source: Some( Error { occurrence: Some( \"examples/example.rs:25:13\", ), kind: Func2Error(func2 error: calling func3), source: Some( Error { occurrence: Some( \"examples/example.rs:18:13\", ), kind: (passed error), source: Some( Error { occurrence: Some( \"examples/example.rs:13:18\", ), kind: \"Error reading 'foo.txt'\", source: Some( Kind( NotFound, ), ), }, ), }, ), }, ),\n}","breadcrumbs":"chainerror » Multiple Output Formats","id":"1","title":"Multiple Output Formats"},"10":{"body":"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 chainerror::Context as _; use std::error::Error;\nuse std::io; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(())\n} fn func2() -> Result<(), Box> { let filename = \"foo.txt\"; do_some_io().context(format!(\"Error reading '{}'\", filename))?; Ok(())\n} fn func1() -> Result<(), Box> { if let Err(e) = func2() { if let Some(s) = e.source() { eprintln!(\"func2 failed because of '{}'\", s); Err(e).context(\"func1 error\")?; } } Ok(())\n} fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!(\"{}\", e); std::process::exit(1); } Ok(())\n}\n# #[allow(dead_code)]\n# mod chainerror {\n# #![doc = include_str!(\"../README.md\")]\n# #![deny(clippy::all)]\n# #![allow(clippy::needless_doctest_main)]\n# #![deny(missing_docs)]\n# # use std::any::TypeId;\n# use std::error::Error as StdError;\n# use std::fmt::{Debug, Display, Formatter};\n# use std::panic::Location;\n# # /// chains an inner error kind `T` with a causing error\n# pub struct Error {\n# occurrence: Option,\n# kind: T,\n# error_cause: Option>,\n# }\n# # /// convenience type alias\n# pub type Result = std::result::Result>;\n# # impl Error {\n# /// Use the `context()` or `map_context()` Result methods instead of calling this directly\n# #[inline]\n# pub fn new(\n# kind: T,\n# error_cause: Option>,\n# occurrence: Option,\n# ) -> Self {\n# Self {\n# occurrence,\n# kind,\n# error_cause,\n# }\n# }\n# # /// return the root cause of the error chain, if any exists\n# pub fn root_cause(&self) -> Option<&(dyn StdError + 'static)> {\n# self.iter().last()\n# }\n# # /// Find the first error cause of type U, if any exists\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// use chainerror::Context as _;\n# /// use chainerror::ErrorDown as _;\n# /// use std::error::Error;\n# /// use std::io;\n# ///\n# /// fn do_some_io() -> Result<(), Box> {\n# /// Err(io::Error::from(io::ErrorKind::NotFound))?;\n# /// Ok(())\n# /// }\n# ///\n# /// chainerror::str_context!(Func2Error);\n# ///\n# /// fn func2() -> Result<(), Box> {\n# /// let filename = \"foo.txt\";\n# /// do_some_io().context(Func2Error(format!(\"Error reading '{}'\", filename)))?;\n# /// Ok(())\n# /// }\n# ///\n# /// chainerror::str_context!(Func1Error);\n# ///\n# /// fn func1() -> Result<(), Box> {\n# /// func2().context(Func1Error::new(\"func1 error\"))?;\n# /// Ok(())\n# /// }\n# ///\n# /// if let Err(e) = func1() {\n# /// if let Some(f1err) = e.downcast_chain_ref::() {\n# /// assert!(f1err.find_cause::().is_some());\n# ///\n# /// assert!(f1err.find_chain_cause::().is_some());\n# /// }\n# /// # else {\n# /// # panic!();\n# /// # }\n# /// }\n# /// # else {\n# /// # unreachable!();\n# /// # }\n# /// ```\n# #[inline]\n# pub fn find_cause(&self) -> Option<&U> {\n# self.iter()\n# .filter_map(::downcast_ref::)\n# .next()\n# }\n# # /// Find the first error cause of type [`Error`](Error), if any exists\n# ///\n# /// Same as `find_cause`, but hides the [`Error`](Error) implementation internals\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// # chainerror::str_context!(FooError);\n# /// # let err = chainerror::Error::new(String::new(), None, None);\n# /// // Instead of writing\n# /// err.find_cause::>();\n# ///\n# /// // leave out the chainerror::Error implementation detail\n# /// err.find_chain_cause::();\n# /// ```\n# #[inline]\n# pub fn find_chain_cause(&self) -> Option<&Error> {\n# self.iter()\n# .filter_map(::downcast_ref::>)\n# .next()\n# }\n# # /// Find the first error cause of type [`Error`](Error) or `U`, if any exists and return `U`\n# ///\n# /// Same as `find_cause` and `find_chain_cause`, but hides the [`Error`](Error) implementation internals\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// # chainerror::str_context!(FooErrorKind);\n# /// # let err = chainerror::Error::new(String::new(), None, None);\n# /// // Instead of writing\n# /// err.find_cause::>();\n# /// // and/or\n# /// err.find_chain_cause::();\n# /// // and/or\n# /// err.find_cause::();\n# ///\n# /// // leave out the chainerror::Error implementation detail\n# /// err.find_kind_or_cause::();\n# /// ```\n# #[inline]\n# pub fn find_kind_or_cause(&self) -> Option<&U> {\n# self.iter()\n# .filter_map(|e| {\n# e.downcast_ref::>()\n# .map(|e| e.kind())\n# .or_else(|| e.downcast_ref::())\n# })\n# .next()\n# }\n# # /// Return a reference to T of [`Error`](Error)\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// use chainerror::Context as _;\n# /// use std::error::Error;\n# /// use std::io;\n# ///\n# /// fn do_some_io() -> Result<(), Box> {\n# /// Err(io::Error::from(io::ErrorKind::NotFound))?;\n# /// Ok(())\n# /// }\n# ///\n# /// chainerror::str_context!(Func2Error);\n# ///\n# /// fn func2() -> Result<(), Box> {\n# /// let filename = \"foo.txt\";\n# /// do_some_io().context(Func2Error(format!(\"Error reading '{}'\", filename)))?;\n# /// Ok(())\n# /// }\n# ///\n# /// #[derive(Debug)]\n# /// enum Func1ErrorKind {\n# /// Func2,\n# /// IO(String),\n# /// }\n# ///\n# /// /// impl ::std::fmt::Display for Func1ErrorKind {…}\n# /// # impl ::std::fmt::Display for Func1ErrorKind {\n# /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {\n# /// # match self {\n# /// # Func1ErrorKind::Func2 => write!(f, \"func1 error calling func2\"),\n# /// # Func1ErrorKind::IO(filename) => write!(f, \"Error reading '{}'\", filename),\n# /// # }\n# /// # }\n# /// # }\n# ///\n# /// fn func1() -> chainerror::Result<(), Func1ErrorKind> {\n# /// func2().context(Func1ErrorKind::Func2)?;\n# /// do_some_io().context(Func1ErrorKind::IO(\"bar.txt\".into()))?;\n# /// Ok(())\n# /// }\n# ///\n# /// if let Err(e) = func1() {\n# /// match e.kind() {\n# /// Func1ErrorKind::Func2 => {}\n# /// Func1ErrorKind::IO(filename) => panic!(),\n# /// }\n# /// }\n# /// # else {\n# /// # unreachable!();\n# /// # }\n# /// ```\n# #[inline]\n# pub fn kind(&self) -> &T {\n# &self.kind\n# }\n# # /// Returns an Iterator over all error causes/sources\n# ///\n# /// # Example\n# #[inline]\n# pub fn iter(&self) -> impl Iterator- {\n# ErrorIter {\n# current: Some(self),\n# }\n# }\n# }\n# # /// Convenience methods for `Result<>` to turn the error into a decorated [`Error`](Error)\n# pub trait Context>> {\n# /// Decorate the error with a `kind` of type `T` and the source `Location`\n# fn context(self, kind: T) -> std::result::Result>;\n# # /// Decorate the error just with the source `Location`\n# fn annotate(self) -> std::result::Result>;\n# # /// Decorate the `error` with a `kind` of type `T` produced with a `FnOnce(&error)` and the source `Location`\n# fn map_context T>(\n# self,\n# op: F,\n# ) -> std::result::Result>;\n# }\n# # /// Convenience type to just decorate the error with the source `Location`\n# pub struct AnnotatedError(());\n# # impl Display for AnnotatedError {\n# fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n# write!(f, \"(passed error)\")\n# }\n# }\n# # impl Debug for AnnotatedError {\n# fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n# write!(f, \"(passed error)\")\n# }\n# }\n# # impl>> Context\n# for std::result::Result\n# {\n# #[track_caller]\n# #[inline]\n# fn context(self, kind: T) -> std::result::Result> {\n# match self {\n# Ok(t) => Ok(t),\n# Err(error_cause) => Err(Error::new(\n# kind,\n# Some(error_cause.into()),\n# Some(Location::caller().to_string()),\n# )),\n# }\n# }\n# # #[track_caller]\n# #[inline]\n# fn annotate(self) -> std::result::Result> {\n# match self {\n# Ok(t) => Ok(t),\n# Err(error_cause) => Err(Error::new(\n# AnnotatedError(()),\n# Some(error_cause.into()),\n# Some(Location::caller().to_string()),\n# )),\n# }\n# }\n# # #[track_caller]\n# #[inline]\n# fn map_context T>(\n# self,\n# op: F,\n# ) -> std::result::Result> {\n# match self {\n# Ok(t) => Ok(t),\n# Err(error_cause) => {\n# let kind = op(&error_cause);\n# Err(Error::new(\n# kind,\n# Some(error_cause.into()),\n# Some(Location::caller().to_string()),\n# ))\n# }\n# }\n# }\n# }\n# # /// An iterator over all error causes/sources\n# pub struct ErrorIter<'a> {\n# current: Option<&'a (dyn StdError + 'static)>,\n# }\n# # impl<'a> Iterator for ErrorIter<'a> {\n# type Item = &'a (dyn StdError + 'static);\n# # #[inline]\n# fn next(&mut self) -> Option {\n# let current = self.current;\n# self.current = self.current.and_then(StdError::source);\n# current\n# }\n# }\n# # impl std::ops::Deref for Error {\n# type Target = T;\n# # #[inline]\n# fn deref(&self) -> &Self::Target {\n# &self.kind\n# }\n# }\n# # /// Convenience trait to hide the [`Error`](Error) implementation internals\n# pub trait ErrorDown {\n# /// Test if of type `Error`\n# fn is_chain(&self) -> bool;\n# /// Downcast to a reference of `Error`\n# fn downcast_chain_ref(&self) -> Option<&Error>;\n# /// Downcast to a mutable reference of `Error`\n# fn downcast_chain_mut(&mut self) -> Option<&mut Error>;\n# /// Downcast to T of `Error`\n# fn downcast_inner_ref(&self) -> Option<&T>;\n# /// Downcast to T mutable reference of `Error`\n# fn downcast_inner_mut(&mut self) -> Option<&mut T>;\n# }\n# # impl ErrorDown for Error {\n# #[inline]\n# fn is_chain(&self) -> bool {\n# TypeId::of::() == TypeId::of::()\n# }\n# # #[inline]\n# fn downcast_chain_ref(&self) -> Option<&Error> {\n# if self.is_chain::() {\n# #[allow(clippy::cast_ptr_alignment)]\n# unsafe {\n# #[allow(trivial_casts)]\n# Some(*(self as *const dyn StdError as *const &Error))\n# }\n# } else {\n# None\n# }\n# }\n# # #[inline]\n# fn downcast_chain_mut(&mut self) -> Option<&mut Error> {\n# if self.is_chain::() {\n# #[allow(clippy::cast_ptr_alignment)]\n# unsafe {\n# #[allow(trivial_casts)]\n# Some(&mut *(self as *mut dyn StdError as *mut &mut Error))\n# }\n# } else {\n# None\n# }\n# }\n# #[inline]\n# fn downcast_inner_ref(&self) -> Option<&T> {\n# if self.is_chain::() {\n# #[allow(clippy::cast_ptr_alignment)]\n# unsafe {\n# #[allow(trivial_casts)]\n# Some(&(*(self as *const dyn StdError as *const &Error)).kind)\n# }\n# } else {\n# None\n# }\n# }\n# # #[inline]\n# fn downcast_inner_mut(&mut self) -> Option<&mut T> {\n# if self.is_chain::() {\n# #[allow(clippy::cast_ptr_alignment)]\n# unsafe {\n# #[allow(trivial_casts)]\n# Some(&mut (*(self as *mut dyn StdError as *mut &mut Error)).kind)\n# }\n# } else {\n# None\n# }\n# }\n# }\n# # impl ErrorDown for dyn StdError + 'static {\n# #[inline]\n# fn is_chain(&self) -> bool {\n# self.is::>()\n# }\n# # #[inline]\n# fn downcast_chain_ref(&self) -> Option<&Error> {\n# self.downcast_ref::>()\n# }\n# # #[inline]\n# fn downcast_chain_mut(&mut self) -> Option<&mut Error> {\n# self.downcast_mut::>()\n# }\n# # #[inline]\n# fn downcast_inner_ref(&self) -> Option<&T> {\n# self.downcast_ref::()\n# .or_else(|| self.downcast_ref::>().map(|e| e.kind()))\n# }\n# # #[inline]\n# fn downcast_inner_mut(&mut self) -> Option<&mut T> {\n# if self.is::() {\n# return self.downcast_mut::();\n# }\n# # self.downcast_mut::>()\n# .and_then(|e| e.downcast_inner_mut::())\n# }\n# }\n# # impl ErrorDown for dyn StdError + 'static + Send {\n# #[inline]\n# fn is_chain(&self) -> bool {\n# self.is::>()\n# }\n# # #[inline]\n# fn downcast_chain_ref(&self) -> Option<&Error> {\n# self.downcast_ref::>()\n# }\n# # #[inline]\n# fn downcast_chain_mut(&mut self) -> Option<&mut Error> {\n# self.downcast_mut::>()\n# }\n# # #[inline]\n# fn downcast_inner_ref(&self) -> Option<&T> {\n# self.downcast_ref::()\n# .or_else(|| self.downcast_ref::>().map(|e| e.kind()))\n# }\n# # #[inline]\n# fn downcast_inner_mut(&mut self) -> Option<&mut T> {\n# if self.is::() {\n# return self.downcast_mut::();\n# }\n# # self.downcast_mut::>()\n# .and_then(|e| e.downcast_inner_mut::())\n# }\n# }\n# # impl ErrorDown for dyn StdError + 'static + Send + Sync {\n# #[inline]\n# fn is_chain(&self) -> bool {\n# self.is::>()\n# }\n# # #[inline]\n# fn downcast_chain_ref(&self) -> Option<&Error> {\n# self.downcast_ref::>()\n# }\n# # #[inline]\n# fn downcast_chain_mut(&mut self) -> Option<&mut Error> {\n# self.downcast_mut::>()\n# }\n# # #[inline]\n# fn downcast_inner_ref(&self) -> Option<&T> {\n# self.downcast_ref::()\n# .or_else(|| self.downcast_ref::>().map(|e| e.kind()))\n# }\n# # #[inline]\n# fn downcast_inner_mut(&mut self) -> Option<&mut T> {\n# if self.is::() {\n# return self.downcast_mut::();\n# }\n# # self.downcast_mut::>()\n# .and_then(|e| e.downcast_inner_mut::())\n# }\n# }\n# # impl StdError for Error {\n# #[inline]\n# fn source(&self) -> Option<&(dyn StdError + 'static)> {\n# self.error_cause\n# .as_ref()\n# .map(|e| e.as_ref() as &(dyn StdError + 'static))\n# }\n# }\n# # impl StdError for &mut Error {\n# #[inline]\n# fn source(&self) -> Option<&(dyn StdError + 'static)> {\n# self.error_cause\n# .as_ref()\n# .map(|e| e.as_ref() as &(dyn StdError + 'static))\n# }\n# }\n# # impl Display for Error {\n# #[inline]\n# fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n# write!(f, \"{}\", self.kind)?;\n# # if f.alternate() {\n# if let Some(e) = self.source() {\n# write!(f, \"\\nCaused by:\\n {:#}\", &e)?;\n# }\n# }\n# # Ok(())\n# }\n# }\n# # impl Debug for Error {\n# #[inline]\n# fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n# if f.alternate() {\n# let mut f = f.debug_struct(&format!(\"Error<{}>\", std::any::type_name::()));\n# # let f = f\n# .field(\"occurrence\", &self.occurrence)\n# .field(\"kind\", &self.kind)\n# .field(\"source\", &self.source());\n# # f.finish()\n# } else {\n# if let Some(ref o) = self.occurrence {\n# write!(f, \"{}: \", o)?;\n# }\n# # if TypeId::of::() == TypeId::of::()\n# || TypeId::of::<&str>() == TypeId::of::()\n# {\n# Display::fmt(&self.kind, f)?;\n# } else {\n# Debug::fmt(&self.kind, f)?;\n# }\n# # if let Some(e) = self.source() {\n# write!(f, \"\\nCaused by:\\n{:?}\", &e)?;\n# }\n# Ok(())\n# }\n# }\n# }\n# # impl From for Error\n# where\n# T: 'static + Display + Debug,\n# {\n# #[track_caller]\n# #[inline]\n# fn from(e: T) -> Error {\n# Error::new(e, None, Some(Location::caller().to_string()))\n# }\n# }\n# /// Convenience macro to create a \"new type\" T(String) and implement Display + Debug for T\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// # use chainerror::Context as _;\n# /// # use chainerror::ErrorDown as _;\n# /// # use std::error::Error;\n# /// # use std::io;\n# /// # use std::result::Result;\n# /// # fn do_some_io() -> Result<(), Box> {\n# /// # Err(io::Error::from(io::ErrorKind::NotFound))?;\n# /// # Ok(())\n# /// # }\n# /// chainerror::str_context!(Func2Error);\n# ///\n# /// fn func2() -> chainerror::Result<(), Func2Error> {\n# /// let filename = \"foo.txt\";\n# /// do_some_io().context(Func2Error(format!(\"Error reading '{}'\", filename)))?;\n# /// Ok(())\n# /// }\n# ///\n# /// chainerror::str_context!(Func1Error);\n# ///\n# /// fn func1() -> Result<(), Box> {\n# /// func2().context(Func1Error::new(\"func1 error\"))?;\n# /// Ok(())\n# /// }\n# /// # if let Err(e) = func1() {\n# /// # if let Some(f1err) = e.downcast_chain_ref::() {\n# /// # assert!(f1err.find_cause::>().is_some());\n# /// # assert!(f1err.find_chain_cause::().is_some());\n# /// # } else {\n# /// # panic!();\n# /// # }\n# /// # } else {\n# /// # unreachable!();\n# /// # }\n# /// ```\n# #[macro_export]\n# macro_rules! str_context {\n# ($e:ident) => {\n# #[derive(Clone)]\n# pub struct $e(pub String);\n# impl $e {\n# pub fn new>(s: S) -> Self {\n# $e(s.into())\n# }\n# }\n# impl ::std::fmt::Display for $e {\n# fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {\n# write!(f, \"{}\", self.0)\n# }\n# }\n# impl ::std::fmt::Debug for $e {\n# fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {\n# write!(f, \"{}({})\", stringify!($e), self.0)\n# }\n# }\n# impl ::std::error::Error for $e {}\n# };\n# }\n# # /// Derive an Error for an ErrorKind, which wraps a [`Error`](Error) and implements a `kind()` method\n# ///\n# /// It basically hides [`Error`](Error) to the outside and only exposes the [`kind()`](Error::kind)\n# /// method.\n# ///\n# /// Error::kind() returns the ErrorKind\n# /// Error::source() returns the parent error\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// use chainerror::Context as _;\n# /// use std::io;\n# ///\n# /// fn do_some_io(_f: &str) -> std::result::Result<(), io::Error> {\n# /// return Err(io::Error::from(io::ErrorKind::NotFound));\n# /// }\n# ///\n# /// #[derive(Debug, Clone)]\n# /// pub enum ErrorKind {\n# /// IO(String),\n# /// FatalError(String),\n# /// Unknown,\n# /// }\n# ///\n# /// chainerror::err_kind!(Error, ErrorKind);\n# ///\n# /// impl std::fmt::Display for ErrorKind {\n# /// fn fmt(&self, f: &mut ::std::fmt::Formatter) -> std::fmt::Result {\n# /// match self {\n# /// ErrorKind::FatalError(e) => write!(f, \"fatal error {}\", e),\n# /// ErrorKind::Unknown => write!(f, \"unknown error\"),\n# /// ErrorKind::IO(filename) => write!(f, \"Error reading '{}'\", filename),\n# /// }\n# /// }\n# /// }\n# ///\n# /// impl ErrorKind {\n# /// fn from_io_error(e: &io::Error, f: String) -> Self {\n# /// match e.kind() {\n# /// io::ErrorKind::BrokenPipe => panic!(\"Should not happen\"),\n# /// io::ErrorKind::ConnectionReset => {\n# /// ErrorKind::FatalError(format!(\"While reading `{}`: {}\", f, e))\n# /// }\n# /// _ => ErrorKind::IO(f),\n# /// }\n# /// }\n# /// }\n# ///\n# /// impl From<&io::Error> for ErrorKind {\n# /// fn from(e: &io::Error) -> Self {\n# /// ErrorKind::IO(format!(\"{}\", e))\n# /// }\n# /// }\n# ///\n# /// pub fn func1() -> std::result::Result<(), Error> {\n# /// let filename = \"bar.txt\";\n# ///\n# /// do_some_io(filename).map_context(|e| ErrorKind::from_io_error(e, filename.into()))?;\n# /// do_some_io(filename).map_context(|e| ErrorKind::IO(filename.into()))?;\n# /// do_some_io(filename).map_context(|e| ErrorKind::from(e))?;\n# /// Ok(())\n# /// }\n# /// ```\n# #[macro_export]\n# macro_rules! err_kind {\n# ($e:ident, $k:ident) => {\n# pub struct $e($crate::Error<$k>);\n# # impl $e {\n# pub fn kind(&self) -> &$k {\n# self.0.kind()\n# }\n# }\n# # impl From<$k> for $e {\n# fn from(e: $k) -> Self {\n# $e($crate::Error::new(e, None, None))\n# }\n# }\n# # impl From<$crate::Error<$k>> for $e {\n# fn from(e: $crate::Error<$k>) -> Self {\n# $e(e)\n# }\n# }\n# # impl From<&$e> for $k\n# where\n# $k: Clone,\n# {\n# fn from(e: &$e) -> Self {\n# e.kind().clone()\n# }\n# }\n# # impl std::error::Error for $e {\n# fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {\n# self.0.source()\n# }\n# }\n# # impl std::fmt::Display for $e {\n# fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n# std::fmt::Display::fmt(&self.0, f)\n# }\n# }\n# # impl std::fmt::Debug for $e {\n# fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {\n# std::fmt::Debug::fmt(&self.0, f)\n# }\n# }\n# };\n# }\n# } Note, that because we changed the output of the error in main() from Debug to Display, we don't see the error backtrace with filename and line number. To use the Display backtrace, you have to use the alternative display format output {:#}.","breadcrumbs":"The source() of Errors » The source() of Errors","id":"10","title":"The source() of Errors"},"11":{"body":"std::error::Error comes with some helper methods to get to the original object of the &(dyn Error + 'static) returned by .source(). pub fn downcast_ref(&self) -> Option<&T>\npub fn downcast_mut(&mut self) -> Option<&mut T> This is how it looks like, when using those: #![allow(clippy::single_match)]\n#![allow(clippy::redundant_pattern_matching)] use chainerror::Context as _; use std::error::Error;\nuse std::io; fn do_some_io() -> Result<(), Box> { Err(io::Error::from(io::ErrorKind::NotFound))?; Ok(())\n} fn func2() -> Result<(), Box> { let filename = \"foo.txt\"; do_some_io().context(format!(\"Error reading '{}'\", filename))?; Ok(())\n} fn func1() -> Result<(), Box> { func2().context(\"func1 error\")?; Ok(())\n} fn main() -> Result<(), Box> { if let Err(e) = func1() { eprintln!(\"Error: {}\", e); let mut s: &(dyn Error) = e.as_ref(); while let Some(c) = s.source() { if let Some(ioerror) = c.downcast_ref::() { 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; } std::process::exit(1); } Ok(())\n}\n# #[allow(dead_code)]\n# mod chainerror {\n# #![doc = include_str!(\"../README.md\")]\n# #![deny(clippy::all)]\n# #![allow(clippy::needless_doctest_main)]\n# #![deny(missing_docs)]\n# # use std::any::TypeId;\n# use std::error::Error as StdError;\n# use std::fmt::{Debug, Display, Formatter};\n# use std::panic::Location;\n# # /// chains an inner error kind `T` with a causing error\n# pub struct Error {\n# occurrence: Option,\n# kind: T,\n# error_cause: Option>,\n# }\n# # /// convenience type alias\n# pub type Result = std::result::Result>;\n# # impl Error {\n# /// Use the `context()` or `map_context()` Result methods instead of calling this directly\n# #[inline]\n# pub fn new(\n# kind: T,\n# error_cause: Option>,\n# occurrence: Option,\n# ) -> Self {\n# Self {\n# occurrence,\n# kind,\n# error_cause,\n# }\n# }\n# # /// return the root cause of the error chain, if any exists\n# pub fn root_cause(&self) -> Option<&(dyn StdError + 'static)> {\n# self.iter().last()\n# }\n# # /// Find the first error cause of type U, if any exists\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// use chainerror::Context as _;\n# /// use chainerror::ErrorDown as _;\n# /// use std::error::Error;\n# /// use std::io;\n# ///\n# /// fn do_some_io() -> Result<(), Box> {\n# /// Err(io::Error::from(io::ErrorKind::NotFound))?;\n# /// Ok(())\n# /// }\n# ///\n# /// chainerror::str_context!(Func2Error);\n# ///\n# /// fn func2() -> Result<(), Box> {\n# /// let filename = \"foo.txt\";\n# /// do_some_io().context(Func2Error(format!(\"Error reading '{}'\", filename)))?;\n# /// Ok(())\n# /// }\n# ///\n# /// chainerror::str_context!(Func1Error);\n# ///\n# /// fn func1() -> Result<(), Box> {\n# /// func2().context(Func1Error::new(\"func1 error\"))?;\n# /// Ok(())\n# /// }\n# ///\n# /// if let Err(e) = func1() {\n# /// if let Some(f1err) = e.downcast_chain_ref::() {\n# /// assert!(f1err.find_cause::().is_some());\n# ///\n# /// assert!(f1err.find_chain_cause::().is_some());\n# /// }\n# /// # else {\n# /// # panic!();\n# /// # }\n# /// }\n# /// # else {\n# /// # unreachable!();\n# /// # }\n# /// ```\n# #[inline]\n# pub fn find_cause(&self) -> Option<&U> {\n# self.iter()\n# .filter_map(::downcast_ref::)\n# .next()\n# }\n# # /// Find the first error cause of type [`Error`](Error), if any exists\n# ///\n# /// Same as `find_cause`, but hides the [`Error`](Error) implementation internals\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// # chainerror::str_context!(FooError);\n# /// # let err = chainerror::Error::new(String::new(), None, None);\n# /// // Instead of writing\n# /// err.find_cause::>();\n# ///\n# /// // leave out the chainerror::Error implementation detail\n# /// err.find_chain_cause::();\n# /// ```\n# #[inline]\n# pub fn find_chain_cause(&self) -> Option<&Error> {\n# self.iter()\n# .filter_map(::downcast_ref::>)\n# .next()\n# }\n# # /// Find the first error cause of type [`Error`](Error) or `U`, if any exists and return `U`\n# ///\n# /// Same as `find_cause` and `find_chain_cause`, but hides the [`Error`](Error) implementation internals\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// # chainerror::str_context!(FooErrorKind);\n# /// # let err = chainerror::Error::new(String::new(), None, None);\n# /// // Instead of writing\n# /// err.find_cause::>();\n# /// // and/or\n# /// err.find_chain_cause::();\n# /// // and/or\n# /// err.find_cause::();\n# ///\n# /// // leave out the chainerror::Error implementation detail\n# /// err.find_kind_or_cause::();\n# /// ```\n# #[inline]\n# pub fn find_kind_or_cause(&self) -> Option<&U> {\n# self.iter()\n# .filter_map(|e| {\n# e.downcast_ref::>()\n# .map(|e| e.kind())\n# .or_else(|| e.downcast_ref::())\n# })\n# .next()\n# }\n# # /// Return a reference to T of [`Error`](Error)\n# ///\n# /// # Examples\n# ///\n# /// ```rust\n# /// use chainerror::Context as _;\n# /// use std::error::Error;\n# /// use std::io;\n# ///\n# /// fn do_some_io() -> Result<(), Box> {\n# /// Err(io::Error::from(io::ErrorKind::NotFound))?;\n# /// Ok(())\n# /// }\n# ///\n# /// chainerror::str_context!(Func2Error);\n# ///\n# /// fn func2() -> Result<(), Box> {\n# /// let filename = \"foo.txt\";\n# /// do_some_io().context(Func2Error(format!(\"Error reading '{}'\", filename)))?;\n# /// Ok(())\n# /// }\n# ///\n# /// #[derive(Debug)]\n# /// enum Func1ErrorKind {\n# /// Func2,\n# /// IO(String),\n# /// }\n# ///\n# /// /// impl ::std::fmt::Display for Func1ErrorKind {…}\n# /// # impl ::std::fmt::Display for Func1ErrorKind {\n# /// # fn fmt(&self, f: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {\n# /// # match self {\n# /// # Func1ErrorKind::Func2 => write!(f, \"func1 error calling func2\"),\n# /// # Func1ErrorKind::IO(filename) => write!(f, \"Error reading '{}'\", filename),\n# /// # }\n# /// # }\n# /// # }\n# ///\n# /// fn func1() -> chainerror::Result<(), Func1ErrorKind> {\n# /// func2().context(Func1ErrorKind::Func2)?;\n# /// do_some_io().context(Func1ErrorKind::IO(\"bar.txt\".into()))?;\n# /// Ok(())\n# /// }\n# ///\n# /// if let Err(e) = func1() {\n# /// match e.kind() {\n# /// Func1ErrorKind::Func2 => {}\n# /// Func1ErrorKind::IO(filename) => panic!(),\n# /// }\n# /// }\n# /// # else {\n# /// # unreachable!();\n# /// # }\n# /// ```\n# #[inline]\n# pub fn kind(&self) -> &T {\n# &self.kind\n# }\n# # /// Returns an Iterator over all error causes/sources\n# ///\n# /// # Example\n# #[inline]\n# pub fn iter(&self) -> impl Iterator
- {\n# ErrorIter {\n# current: Some(self),\n# }\n# }\n# }\n# # /// Convenience methods for `Result<>` to turn the error into a decorated [`Error`](Error)\n# pub trait Context>> {\n# /// Decorate the error with a `kind` of type `T` and the source `Location`\n# fn context(self, kind: T) -> std::result::Result>;\n# # /// Decorate the error just with the source `Location`\n# fn annotate(self) -> std::result::Result>;\n# # /// Decorate the `error` with a `kind` of type `T` produced with a `FnOnce(&error)` and the source `Location`\n# fn map_context T>(\n# self,\n# op: F,\n# ) -> std::result::Result>;\n# }\n# # /// Convenience type to just decorate the error with the source `Location`\n# pub struct AnnotatedError(());\n# # impl Display for AnnotatedError {\n# fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n# write!(f, \"(passed error)\")\n# }\n# }\n# # impl Debug for AnnotatedError {\n# fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {\n# write!(f, \"(passed error)\")\n# }\n# }\n# # impl>> Context\n# for std::result::Result\n# {\n# #[track_caller]\n# #[inline]\n# fn context(self, kind: T) -> std::result::Result> {\n# match self {\n# Ok(t) => Ok(t),\n# Err(error_cause) => Err(Error::new(\n# kind,\n# Some(error_cause.into()),\n# Some(Location::caller().to_string()),\n# )),\n# }\n# }\n# # #[track_caller]\n# #[inline]\n# fn annotate(self) -> std::result::Result> {\n# match self {\n# Ok(t) => Ok(t),\n# Err(error_cause) => Err(Error::new(\n# AnnotatedError(()),\n# Some(error_cause.into()),\n# Some(Location::caller().to_string()),\n# )),\n# }\n# }\n# # #[track_caller]\n# #[inline]\n# fn map_context T>(\n# self,\n# op: F,\n# ) -> std::result::Result> {\n# match self {\n# Ok(t) => Ok(t),\n# Err(error_cause) => {\n# let kind = op(&error_cause);\n# Err(Error::new(\n# kind,\n# Some(error_cause.into()),\n# Some(Location::caller().to_string()),\n# ))\n# }\n# }\n# }\n# }\n# # /// An iterator over all error causes/sources\n# pub struct ErrorIter<'a> {\n# current: Option<&'a (dyn StdError + 'static)>,\n# }\n# # impl<'a> Iterator for ErrorIter<'a> {\n# type Item = &'a (dyn StdError + 'static);\n# # #[inline]\n# fn next(&mut self) -> Option {\n# let current = self.current;\n# self.current = self.current.and_then(StdError::source);\n# current\n# }\n# }\n# # impl std::ops::Deref for Error {\n# type Target = T;\n# # #[inline]\n# fn deref(&self) -> &Self::Target {\n# &self.kind\n# }\n# }\n# # /// Convenience trait to hide the [`Error`](Error) implementation internals\n# pub trait ErrorDown {\n# /// Test if of type `Error`\n# fn is_chain(&self) -> bool;\n# /// Downcast to a reference of `Error`\n# fn downcast_chain_ref(&self) -> Option<&Error>;\n# /// Downcast to a mutable reference of `Error`\n# fn downcast_chain_mut(&mut self) -> Option<&mut Error>;\n# /// Downcast to T of `Error`\n# fn downcast_inner_ref(&self) -> Option<&T>;\n# /// Downcast to T mutable reference of `Error`\n# fn downcast_inner_mut(&mut self) -> Option<&mut T>;\n# }\n# # impl ErrorDown for Error {\n# #[inline]\n# fn is_chain(&self) -> bool {\n# TypeId::of::() == TypeId::of::()\n# }\n# # #[inline]\n# fn downcast_chain_ref