dtolnay/anyhow

Should `anyhow::Error::chain` return `dyn Error + Send + Sync`

Opened this issue · 2 comments

anyhow::Error only supports wrapping Send + Sync errors, but the API for chainreturns non-send and non-sync errors.

I suspect this is to mimic the source stdlib API, if so, would it be advisable to introduce a chain_send_sync method?

The same goes for the root_cause API, which with the lack of Send + Sync makes an anyhow conversion non-bijective

What would be the expected behavior if the causes or root cause are not Send + Sync?

// [dependencies]
// anyhow = "1"
// threadbound = "0.1"

use std::fmt::{self, Display};
use threadbound::ThreadBound;

#[derive(Debug)]
struct OuterError {
    inner: ThreadBound<InnerError>,
}

impl std::error::Error for OuterError {
    fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
        let inner = self.inner.get_ref()?;
        Some(inner)
    }
}

impl Display for OuterError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("...")
    }
}

#[derive(Copy, Clone, Debug)]
struct InnerError {
    p: *const (), // not sync
}

impl std::error::Error for InnerError {}

impl Display for InnerError {
    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
        formatter.write_str("...")
    }
}

fn main() {
    let err = f().unwrap_err();
    for e in err.chain() {
        println!("{}", e);
    }
}

fn f() -> anyhow::Result<()> {
    g()?;
    Ok(())
}

fn g() -> Result<(), OuterError> {
    Err(OuterError {
        inner: ThreadBound::new(InnerError { p: 0 as _ }),
    })
}