Closed genbattle closed 2 years ago
I ran into this today in a similar use case. Would a PR be accepted to fix this issue?
I don't plan to experiment with alternative downcasting implementations as part of this crate so I am closing this issue, but I would be willing to consider a working PR if somebody sends one.
@genbattle I tried to do the same, I am also trying to see if I can attach HTTP status code to error. But its kind of not working as expected:
#[cfg(test)]
mod test {
use anyhow::Context;
#[derive(thiserror::Error, Debug)]
enum EFirst {
#[error("yo")]
Yo,
}
fn outer() -> Result<(), anyhow::Error> {
anyhow::Ok(out()?).context(http::StatusCode::CREATED)
}
fn out() -> Result<(), anyhow::Error> {
anyhow::Ok(first()?).context(http::StatusCode::ACCEPTED)
}
fn first() -> Result<(), anyhow::Error> {
Err(EFirst::Yo).context(http::StatusCode::SEE_OTHER)
}
#[test]
fn t() {
let e = outer().unwrap_err();
assert_eq!(
*e.downcast_ref::<http::StatusCode>().unwrap(),
http::StatusCode::SEE_OTHER
);
}
}
I only get the error attached at first level when custom type was converted to anyhow::Error
.
Trying to chain()
with http::StatusCode
doesn’t work as it is not Error
. Then I tried:
#[derive(thiserror::Error, Debug, PartialEq)]
enum Status {
#[error("created")]
Created,
#[error("accepted")]
Accepted,
#[error("see-other")]
SeeOther,
}
fn outer2() -> Result<(), anyhow::Error> {
anyhow::Ok(out2()?).context(Status::Created)
}
fn out2() -> Result<(), anyhow::Error> {
anyhow::Ok(first2()?).context(Status::Accepted)
}
fn first2() -> Result<(), anyhow::Error> {
Err(EFirst::Yo).context(Status::SeeOther)
}
#[test]
fn t2() {
let e = outer2().unwrap_err();
println!("status: {:?}", e.downcast_ref::<Status>());
for cause in e.chain() {
println!("status: {:?}", cause.downcast_ref::<Status>());
}
assert!(false)
}
Which gives:
---- error::test::t2 stdout ----
status: Some(SeeOther)
status: None
status: None
thread 'error::test::t2' panicked at ft-sdk/src/error.rs:105:9:
So the calls like anyhow::Ok(first2()?).context(Status::Accepted)
have no effect.
@dtolnay in anyhow::Context
: Effect on downcasting it says:
Some codebases prefer to use machine-readable context to categorize lower level errors in a way that will be actionable to higher levels of the application
Is this a bug, or I am I misunderstanding something?
First of all thanks to the creators and maintainers of this library, it's been so instrumental in improving Rust's error handling story for me.
In my application I'm using
anyhow::Error
to create a stack of errors and information as an error "unwinds" through callers, then at the API level of my application I peel that back layer by layer usinganyhow::Error::chain
on the returned error. Some of the attached context is a typed error object which specifies more information about how the error should be translated at the API boundary (such as HTTP status codes) but I've found that the&dyn std::error::Error
returned bychain
can't bedowncast
ed to the original type if it was attached to the error usinganyhow::Error::context
.This failure only seems to be related to objects that are attached to the
Error
via thecontext
method, andanyhow::Error::downcast_ref
seems to correctly downcast to an object attached viacontext
(it just only allows me to get the first instance of a class attached multiple times). This seems like a bug in howstd::error::Error::downcast_ref
interacts with the trait objects returned bychain
, which I can only guess is related to the type erasure or wrapping going on within anyhow.Here's a simplified example:
The following code:
Produces:
Whereas I would expect it to produce:
So the original
MyError
object passed toanyhow!
can be downcasted properly, but anyMyError
objects attached viacontext
cannot be.I'm guessing this may have something to do with the fact that
context
doesn't require arguments toimpl std::error::Error
, so there must be some sort of wrapper which is being used to return them fromchain
as an&dyn std::error::Error
. I'm not sure if it was intended to support this use case or not, but it would be great if I could downcast context in the same way I can downcast the original error. It's frustrating thatanyhow::Error::downcast_ref
works on context correctly, butstd::Error::downcast_ref
on the references returned bychain
doesn't.When I did a quick scan of the issue tracker I thought this may be related to #84, but I think that although fixing this issue may also solve that one, they're fundamentally different approaches.