I have a custom tower::Service that I have wrapped in a tower::buffer::Buffer (in order to make it Clone such that I can issue calls to the same underlying service from multiple threads).
My customer tower::Service uses anyhow::Error as it's Error type (and I heavily use anyhow::Context) - and I would like to propagate this Error to the caller of my service.
However, tower::buffer::Buffer seems to always use a .into() on the Error to force it into a boxed error (Box) - and that conversion seems to prevent me from getting back the underlying anyhow::Error regardless of what I do (In particular I am gettinga ll kinds of error if trying to dowcast the type-erased Box back to anyhow::Error - I assume the reason is since its no longer an anyhow::Error but converted by the into() call in Buffer).
Does anyone have any simple suggestion for how to get buffer::Buffer to retuturn an anyhow::Error?
Error::downcast should be able to extract anyhow::Error, but anyhow error may be wrapped in other error types too. Try debug printing {:?} to see how many layers you need to unpeel.
error[E0277]: the trait bound `anyhow::Error: std::error::Error` is not satisfied
--> src\lib.rs:127:55
|
127 | let e: anyhow::Error = match e.downcast::<anyhow::Error>() {
| -------- ^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `util::Error`
| |
| required by a bound introduced by this call
Same problem, doesnt seem to be the type inference that's the problem
(util::Error = anyhow::Error, I have pub use anyhow::Error in util)
I mean it's not possible to downcast dyn Error to anyhow::Error, because the Error::downcast method says it can only return types implementing std Error, and anyhow::Error doesn't implement it.
This limitation seems arbitrary to me, but that's how it's implemented. The conversion from anyhow seems to be one-way only.
Ok thansk - and I assume thatt here is no way to use the downcast methid in Box either? (Eg. not the error downcast) - I ahve tried that but keeps running into various errors with that too..
The conversion from anyhow::Error to Box<dyn Error> is implemented as
impl From<Error> for Box<dyn StdError + Send + Sync + 'static> {
#[cold]
fn from(error: Error) -> Self {
let outer = ManuallyDrop::new(error);
unsafe {
// Use vtable to attach ErrorImpl<E>'s native StdError vtable for
// the right original type E.
(vtable(outer.inner.ptr).object_boxed)(outer.inner)
}
}
}
In other words I think you have to downcast into the type of the error that is stored inside the anyhow::Error rather than downcast to anyhow::Error itself. Some constructors of anyhow::Error store a private type as inner error however like .context() which stores the private DisplayError type.
Thansk, that's interesting - but for me as I understand it means the same since the errors I'm interested in are those with context whcih are then private types so unsable to me anyway?
So I feel a bit supid - but thought I should not it down for potential googlers:
My initial issue was that I wanted to propagate the Contexts associated with the anyhow:Error across the tower::Service boundary - i.e. the error being converted Into a Box.
Turns out that anyhow supports to convert back into an anyhow error including the contexts simply by .map_err(|e| anyhow!(e)) on the recieving side
Previously I had been using anyhow!("{}", e) which obviously formats the error and thus loses the contexts - but learning about the anyhow!(e) form of the macros solves all problems..