dtolnay/anyhow

Allow source errors to expose their own context?

NightEule5 opened this issue · 1 comments

It would be useful if error types could expose context to Anyhow. Say you're writing a library with an error like this:

#[derive(Copy, Debug)]
enum Context {
    Copy,
    Read,
    Write,
    Flush,
    // ...
}

impl fmt::Display for Context {
    // ...
}

#[derive(Debug, thiserror::Error)]
#[error("IO operation failed")]
struct Error {
    #[source]
    source: std::io::Error,
    context: Context
}

impl Error {
    fn context(&self) -> Context {
        self.context
    }
}

With Anyhow, the user could add the context manually like this, assuming they know the error type:

fn something() -> Result<(), Error> {
    // ...
}

fn main() -> anyhow::Result<()> {
    match something() {
        Ok(_) => Ok(()),
        Err(error) => Err(
            anyhow::Error::from(error)
                .context(error.context())
        )
    }
}

It'd be useful for error types to add context like this seamlessly. Maybe using the std Error trait's provide method could help here, but I didn't look too far into it. Adding a specialized trait could work:

// in anyhow
use std::{convert, error, fmt};

pub trait ContextError {
    type Context: fmt::Display;
    fn context(&self) -> Option<Self::Context>;
}

default impl<T: error::Error> ContextError for T {
    type Context = convert::Infallible;
    fn context(&self) -> Option<Self::Context> { None }
}

// usage
impl ContextError for Error {
    type Context = Context;
    fn context(&self) -> Option<Self::Context> {
        Some(self.context)
    }
}

Since specialization is unstable, it'd have to be feature-gated though. Thoughts?

Thanks for the suggestion!

This is not something that I would want to build into this crate, but I would be interested to see it explored further by someone in a different crate.