From 61ad44679915703a1a8f6d8c5d6d055596182008 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sun, 27 Oct 2019 20:57:27 -0700 Subject: [PATCH] Document the interaction of context and downcast --- src/error.rs | 9 ++++- src/lib.rs | 96 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) diff --git a/src/error.rs b/src/error.rs index 350c00b..64ee19e 100644 --- a/src/error.rs +++ b/src/error.rs @@ -255,7 +255,14 @@ impl Error { root_cause } - /// Returns `true` if `E` is the type wrapped by this error object. + /// Returns true if `E` is the type held by this error object. + /// + /// For errors with context, this method returns true if `E` matches the + /// type of the context `C` **or** the type of the error on which the + /// context has been attached. For details about the interaction between + /// context and downcasting, [see here]. + /// + /// [see here]: trait.Context.html#effect-on-downcasting pub fn is(&self) -> bool where E: Display + Debug + Send + Sync + 'static, diff --git a/src/lib.rs b/src/lib.rs index 21e2681..edc5070 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -286,6 +286,8 @@ pub type Result = std::result::Result; /// This trait is sealed and cannot be implemented for types outside of /// `anyhow`. /// +///
+/// /// # Example /// /// ``` @@ -326,6 +328,100 @@ pub type Result = std::result::Result; /// Caused by: /// No such file or directory (os error 2) /// ``` +/// +///
+/// +/// # Effect on downcasting +/// +/// After attaching context of type `C` onto an error of type `E`, the resulting +/// `anyhow::Error` may be downcast to `C` **or** to `E`. +/// +/// That is, in codebases that rely on downcasting, Anyhow's context supports +/// both of the following use cases: +/// +/// - **Attaching context whose type is insignificant onto errors whose type +/// is used in downcasts.** +/// +/// In other error libraries whose context is not designed this way, it can +/// be risky to introduce context to existing code because new context might +/// break existing working downcasts. In Anyhow, any downcast that worked +/// before adding context will continue to work after you add a context, so +/// you should freely add human-readable context to errors wherever it would +/// be helpful. +/// +/// ``` +/// # use anyhow::bail; +/// # use thiserror::Error; +/// # +/// # #[derive(Error, Debug)] +/// # #[error("???")] +/// # struct SuspiciousError; +/// # +/// # fn helper() -> Result<()> { +/// # bail!(SuspiciousError); +/// # } +/// # +/// use anyhow::{Context, Result}; +/// +/// fn do_it() -> Result<()> { +/// helper().context("failed to complete the work")?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unreachable!() +/// } +/// +/// fn main() { +/// let err = do_it().unwrap_err(); +/// if let Some(e) = err.downcast_ref::() { +/// // If helper() returned SuspiciousError, this downcast will +/// // correctly succeed even with the context in between. +/// # return; +/// } +/// # panic!("expected downcast to succeed"); +/// } +/// ``` +/// +/// - **Attaching context whose type is used in downcasts onto errors whose +/// type is insignificant.** +/// +/// Some codebases prefer to use machine-readable context to categorize +/// lower level errors in a way that will be actionable to higher levels of +/// the application. +/// +/// ``` +/// # use anyhow::bail; +/// # use thiserror::Error; +/// # +/// # #[derive(Error, Debug)] +/// # #[error("???")] +/// # struct HelperFailed; +/// # +/// # fn helper() -> Result<()> { +/// # bail!("no such file or directory"); +/// # } +/// # +/// use anyhow::{Context, Result}; +/// +/// fn do_it() -> Result<()> { +/// helper().context(HelperFailed)?; +/// # const IGNORE: &str = stringify! { +/// ... +/// # }; +/// # unreachable!() +/// } +/// +/// fn main() { +/// let err = do_it().unwrap_err(); +/// if let Some(e) = err.downcast_ref::() { +/// // If helper failed, this downcast will succeed because +/// // HelperFailed is the context that has been attached to +/// // that error. +/// # return; +/// } +/// # panic!("expected downcast to succeed"); +/// } +/// ``` pub trait Context: context::private::Sealed { /// Wrap the error value with additional context. fn context(self, context: C) -> Result