You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
This repository has been archived by the owner on Aug 16, 2021. It is now read-only.
I have a requirement that any error chain produced by our system could be serialized into a special "error list" format (basically, a JSON with some standardized fields).
In an ideal world, Fail trait would magically contain a function fn error_info() -> ErrorInfo that I could use to extract information from each error in the chain and serialize it into the list.
There is already a functionality to traverse error chain (fn causes(&self) on Fail), so I will be able to traverse the chain, get those trait objects &Fail for each error and grab the info.
However, I cannot add my custom function to failure::Fail (duh!) and I don't want to design my own error crate.
Another requirement is that I want to have "normal" error enums, in the sense that each library crate of my system would just declare its own error type and make it derive Fail. I don't want some special "error" type used everywhere, etc.
Also, I want to be able to build those chains by using functionality similar to what Context does in failure.
The biggest stumbling block for me now is that how do I do "dynamic" dispatching while traversing that chain? Currently I built a contraption around Context<T> that uses my custom error type:
for cause in error.causes(){ifletSome(err) = cause.downcast_ref::<MyError>(){let info:ExtraInfo = err.get_extra_info();
infos.push(info)}}
I'll try to explain it, but it's a little bit weird. And it does not work exactly the way I want it.
I have my custom trait ExtraInfo
All my error types implement it
I use my custom error type MyError, which contains a Box<ExtraInfo>
Context<T> where T: ExtraInfo is convertible to MyError.
In the end, this extra info scavenging works only if I convert each error into MyError first. So, for example, simply wrapping error and using #[cause] wouldn't work because wrapped error is not "downcastable" to MyError.
You can see that I'm using my custom errors sometimes as context to Context<T> and sometimes as errors themselves, which is awkward (but I think this is similar to the https://boats.gitlab.io/failure/error-errorkind.html ?).
However, I think, I do want some flexibility in the way I chain errors. Sometimes, I want an explicit wrapping, like here:
The former is better suited for libraries (more traditional error handling with specific error types), the latter for the application itself (don't really care about exact error type, fine with just using generic MyError).
I think, I took the wrong turn and what I really want is what I described in the beginning: I want each Fail implementation to optionally provide custom info I need. Basically, I want it to be "castable" to another trait of mine.
I can create such system by making a global map that maps from error type id into virtual table for my own ExtraInfo trait, but I would rather use Rust code to match on interface type versus static global mutable map (mutable, because it needs to be initialized from multiple parts of the system to "register" mappings between type ids into trait "virtual tables").
What I'm asking here, would it make any sense to expand "downcasting" functionality in Fail to support downcasting to traits, by adding the following function to the Fail:
(although, I don't know how to combine this with derive for Fail? should user specify which traits error should be "castable" to?)
Then, this function could be used like err.cast_iface<ExtraInfo> to get TraitObject, which could be transmuted later into &ExtraInfo, by some safe wrapper (which, presumably, should be auto-generated), like below:
TL;DR; what I'm proposing here is extending Fail trait to support to downcasting to a trait.
I have a working prototype which is essentially an Any able to downcast to a trait (with the requirement that all supported interfaces are explicitly listed).
I have a requirement that any error chain produced by our system could be serialized into a special "error list" format (basically, a JSON with some standardized fields).
In an ideal world,
Fail
trait would magically contain a functionfn error_info() -> ErrorInfo
that I could use to extract information from each error in the chain and serialize it into the list.There is already a functionality to traverse error chain (
fn causes(&self)
onFail
), so I will be able to traverse the chain, get those trait objects&Fail
for each error and grab the info.However, I cannot add my custom function to
failure::Fail
(duh!) and I don't want to design my own error crate.Another requirement is that I want to have "normal" error enums, in the sense that each library crate of my system would just declare its own error type and make it derive
Fail
. I don't want some special "error" type used everywhere, etc.Also, I want to be able to build those chains by using functionality similar to what
Context
does infailure
.The biggest stumbling block for me now is that how do I do "dynamic" dispatching while traversing that chain? Currently I built a contraption around
Context<T>
that uses my custom error type:I'll try to explain it, but it's a little bit weird. And it does not work exactly the way I want it.
ExtraInfo
MyError
, which contains aBox<ExtraInfo>
Context<T> where T: ExtraInfo
is convertible toMyError
.In the end, this extra info scavenging works only if I convert each error into
MyError
first. So, for example, simply wrapping error and using#[cause]
wouldn't work because wrapped error is not "downcastable" toMyError
.You can see that I'm using my custom errors sometimes as context to
Context<T>
and sometimes as errors themselves, which is awkward (but I think this is similar to the https://boats.gitlab.io/failure/error-errorkind.html ?).However, I think, I do want some flexibility in the way I chain errors. Sometimes, I want an explicit wrapping, like here:
Sometimes, I want to use literal from
MyHighError
as a context to the lower-level error:The former is better suited for libraries (more traditional error handling with specific error types), the latter for the application itself (don't really care about exact error type, fine with just using generic
MyError
).I think, I took the wrong turn and what I really want is what I described in the beginning: I want each
Fail
implementation to optionally provide custom info I need. Basically, I want it to be "castable" to another trait of mine.I can create such system by making a global map that maps from error type id into virtual table for my own
ExtraInfo
trait, but I would rather use Rust code to match on interface type versus static global mutable map (mutable, because it needs to be initialized from multiple parts of the system to "register" mappings between type ids into trait "virtual tables").What I'm asking here, would it make any sense to expand "downcasting" functionality in
Fail
to support downcasting to traits, by adding the following function to theFail
:The implementation would be a match on
iface
type id, somehow generated automatically:(although, I don't know how to combine this with derive for
Fail
? should user specify which traits error should be "castable" to?)Then, this function could be used like
err.cast_iface<ExtraInfo>
to getTraitObject
, which could be transmuted later into&ExtraInfo
, by some safe wrapper (which, presumably, should be auto-generated), like below:Does it make any sense? Any other suggestions?
The text was updated successfully, but these errors were encountered: