Implement Context for Error
Opened this issue · 0 comments
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
-
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) }) }
-
When handling errors that may map to
anyhow
errors or to other types of errors.For example, I use
anyhow
in conjunction withthiserror
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
-
This makes the cases above more readable.
-
It would be nice to have a unified way to add context to results or errors.
Cons
-
This would complicate the
Context
trait. I can see this not being worth it just to avoid theanyhow::Error::new
call. -
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.