dtolnay / anyhow

Flexible concrete Error type built on std::error::Error
Apache License 2.0
5.38k stars 145 forks source link

Conversion to Box<dyn Error> loses downcast ability #379

Open seanmonstar opened 2 months ago

seanmonstar commented 2 months ago

When I have an anyhow::Error and convert that into a Box<dyn std::error::Error>, it seems that I can no longer downcast it back into anything (neither anyhow::Error nor the original error type). This differs from the behavior of casting the anyhow error to a &(dyn StdError + 'static), which does still allow downcasting to the orignal type.

Perhaps this is a limitation that cannot be solved, but I couldn't find it documented so I wasn't sure if it was known.

use std::fmt;
use std::error::Error as StdError;

type BoxError = Box<dyn StdError + Send + Sync>;

#[derive(Debug)]
struct Sentinel;

impl fmt::Display for Sentinel {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("sentinel")
    }
}

impl StdError for Sentinel {}

let a: anyhow::Error = Sentinel.into();
let ar: &(dyn StdError + 'static) = a.as_ref();
dbg!(ar.is::<Sentinel>()); // true
let b: BoxError = a.into();
dbg!(b.is::<Sentinel>()); // false
//dbg!(b.is::<anyhow::Error>()); // can't do
demosdemon commented 1 week ago

Came across this myself today. I expected that I could massage the Anyhow boxed error back into an anyhow::Error but that also doesn't work.

https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=f35fe71789ec7ac29cb0610a28ef68da

Naively, I thought the from_boxed1 could check to see if the Boxed error is an ErrorImpl, but it can't since object_boxed revives the erased type before boxing2