Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Context for Error #352

Open
mawallace opened this issue Mar 5, 2024 · 0 comments
Open

Implement Context for Error #352

mawallace opened this issue Mar 5, 2024 · 0 comments

Comments

@mawallace
Copy link

mawallace commented Mar 5, 2024

Current State

Error does not implement Context. It has a context method, but no with_context method.

In the documentation for Error::context, it says

The primary reason to use error.context(...) instead of result.context(...) via the Context trait would be if the context needs to depend on some data held by the underlying error:

and gives an example

pub fn parse(path: impl AsRef<Path>) -> Result<T> {
    let file = File::open(&path)?;
    parse_impl(file).map_err(|error| {
        let context = format!(
            "only the first {} lines of {} are valid",
            error.line, path.as_ref().display(),
        );
        anyhow::Error::new(error).context(context)
    })
}

Proposal

Would it be possible to implement Context for types implementing std::error::Error?

Here, instead of always outputting a Result, context and with_context would output the "anyhowed" version of Self (i.e., anyhow::Error for errors, anyhow::Result for result).

Use Cases

  1. There is the use case stated in the docs, where the context depends on the error. The example from the docs would then look like

    pub fn parse(path: impl AsRef<Path>) -> Result<T> {
        let file = File::open(&path)?;
        parse_impl(file).map_err(|error| {
            let context = format!(
                "only the first {} lines of {} are valid",
                error.line, path.as_ref().display(),
            );
            error.context(context)
        })
    }
    
  2. When handling errors that may map to anyhow errors or to other types of errors.

    For example, I use anyhow in conjunction with thiserror to model internal errors on libraries:

    trait Doer {
      fn Do(&self) -> Result<(), DoError>;
    }
    
    #[derive(thiserror::Error, Debug)]
    enum DoError {
      #[error("Some specific error that the client should react to.")]
      SomeError,
    
      #[error("Some other error that the client should react to.")]
      OtherError,
    
      #[error(transparent)]
      Internal(#[from] anyhow::Error),
    }
    

    Sometimes I need to map an error from a dependency into either a specific kind of an error, or an internal error with context. When this arises, I often use some sort of match, e.g.,

    impl Doer for MyDoer {
      fn Do(&self) -> Result<(), DoError> {
        match self.fallible_dependency.call() {
          Ok(_) => Ok(()),
          Err(DependencyError::A) => Err(DoError::SomeError),
          Err(DependencyError::B) => Err(DoError::OtherError),
          Err(_) => Err(anyhow::Error::new(err).context("Some context.").into()),
        }
      }
    }
    

    It would be nice to be able to write this last arm as something like

    Err(_) => Err(err.context("Some context.").into())
    

Pros

  1. This makes the cases above more readable.

  2. It would be nice to have a unified way to add context to results or errors.

Cons

  1. This would complicate the Context trait. I can see this not being worth it just to avoid the anyhow::Error::new call.

  2. with_context may not provide much value since you already know you're in the error case when adding context directly to an error so you don't need to avoid the overhead.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant