anyhow
anyhow copied to clipboard
Pass anyhow::Error where impl Error is expected
I have a function that expects impl Error + Send + Sync + 'static:
fn a(_: impl std::error::Error + Send + Sync + 'static) {
todo!()
}
I would like to call it with an instance of anyhow::Anyhow - for example, obtained through the anyhow! macro. I know that Anyhow doesn't implement std::error::Error, but I also know that it can be easily converted to Box<dyn Error>. However, for some reason, none of the usual tactics seem to work:
fn b() {
a(anyhow!("foo")); // the trait `StdError` is not implemented for `anyhow::Error`
}
fn b() {
a(anyhow!("foo").into()); // cannot infer type for type parameter `impl std::error::Error`
}
fn b() {
let err: Box<dyn std::error::Error + Send + Sync + 'static> = anyhow!("foo").into();
a(err); // expected an implementor of trait `StdError` - consider borrowing
}
fn b() {
let err: Box<dyn std::error::Error + Send + Sync + 'static> = anyhow!("foo").into();
a(&err); // doesn't have a size known at compile-time
}
fn b() {
let err: Box<dyn std::error::Error + Send + Sync + 'static> = anyhow!("foo").into();
a(err.as_ref()); // borrowed value does not live long enough
}
Is there a way to box Anyhow so that the resulting object actually implements Error?
If you need an Error trait impl, you need to use https://github.com/dtolnay/thiserror.
#[derive(Error, Debug)]
#[error(transparent)]
struct Error(#[from] anyhow::Error);
fn b() {
a(Error(anyhow!("foo")));
}
Sure, that would work - but it's quite unwieldy to define an Error struct without additional meaning. Since Anyhow is capable of transforming itself into Box<dyn Error>, I figured that an Error impl is lurking there internally and expected that it could be somehow obtained.
If there is a design principle that prevents this from working, then this issue can be closed. I'd also be curious to learn the reasoning behind it, and it might be interesting to others finding this through google.
@hniksic the reason is that the implementation would be incoherent with the following two existing implementations:
impl<E> From<E> for anyhow::Error where E: std::error::Error + Send + Sync + 'staticimpl From<T> for Tprovided by the standard library
The former is what gives you the ability to use ? on other errors.
@rossmacarthur That's indeed the reason why anyhow::Error doesn't itself implement std::error::Error. I was probably not clear enough, but my intention was to ask the more general question: why is there no way to get something that implements std::error::Error once you're in the possession of an anyhow::Error? After all, such a type must exist internally, otherwise Into<Boxed<dyn Error>> wouldn't be possible.
In the code that prompted this issue, the function accepting E: StdError did it with the best of intentions, its author reasoning that that would be the most generic way of accepting an error value (which is not entirely unreasonable). To call such a function, even an existential type would suffice - e.g. if anyhow::Error had a method into_error(self) -> impl StdError + Send + Sync + 'static, it would be perfect.
So is there any simple way to pass anyhow::Error in a place which expects impl std::error::Error or has a from std::error::Error ?...
Getting thiserror crate involved just for this seems like shooting a fly with bazooka...
https://github.com/dtolnay/anyhow/issues/153#issuecomment-833718851 (or the equivalent handwritten Error impl) is the recommendation.