'Miette - How to specify errors that contain a generic that defaults to Box<dyn Error>?
While this question is about the miette library, it could probably be answered by every experienced Rustacean by looking at the miette sourcecode. It isn't that big, but I wasn't able to extract the answer from it, sadly.
All the following examples assume the dependencies:
miette = { version = "4.4", features = ["fancy"] }
thiserror = "1.0"
I currently have a custom error definition similar to the following in my library tokio-graceful-shutdown:
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum MyError {
#[error("Error containing another error")]
AnError(#[source] Box<dyn Error + Send + Sync>),
}
I would like the source error type to be generic, with a default value of Box<dyn Error>.
My attempts
If I would leave out the miette::Diagnostic, the solution would look like this:
#[derive(Debug, thiserror::Error)]
pub enum MyError<ErrType = Box<dyn Error + Send + Sync>> {
#[error("Error containing another error")]
AnError(#[source] ErrType),
}
If I would leave out the Box<dyn Error> default value, the solution would look like this:
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum MyError<ErrType: 'static + Error> {
#[error("Error containing another error")]
AnError(#[source] ErrType),
}
Sadly, combining the two, it turns out that Box<dyn Error> does NOT implement Error:
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum MyError<ErrType: 'static + Error = Box<dyn Error + Send + Sync>> {
#[error("Error containing another error")]
AnError(#[source] ErrType),
}
|
| pub enum MyError<ErrType: 'static + Error = Box<dyn Error + Send + Sync>> {
| ^^^^^ doesn't have a size known at compile-time
|
= help: the trait `Sized` is not implemented for `(dyn std::error::Error + Send + Sync + 'static)`
= note: required because of the requirements on the impl of `std::error::Error` for `Box<(dyn std::error::Error + Send + Sync + 'static)>`
I suspect, however, that this error message is a red herring and instead, the 'static + Error is incorrect and ErrType needs a different restriction.
The reason why I suspect this is that if I directly use Box<dyn Error> as the source type, it works, as in the first example. That makes me believe that it isn't actually Error that miette::Diagnostic requires to work.
Final question
(which is hopefully not susceptible to the XY problem)
What should be inserted at the <???> placeholder to make the following code compile?
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum MyError<ErrType: <???> = Box<dyn Error + Send + Sync>> {
#[error("Error containing another error")]
AnError(#[source] ErrType),
}
Solution 1:[1]
For why this happens, see Why doesn't Box<dyn Error> implement Error? (by the way, this is not related to miette at all: the error is triggered even without it).
You can use a newtype wrapper to work around this problem:
pub struct BoxedError(pub Box<dyn Error + Send + Sync>);
impl fmt::Debug for BoxedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
}
impl fmt::Display for BoxedError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
}
impl Error for BoxedError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
self.0.source()
}
#[allow(deprecated)]
fn description(&self) -> Option<&str> {
self.0.description()
}
#[allow(deprecated)]
fn cause(&self) -> Option<&dyn Error> {
self.0.cause()
}
}
#[derive(Debug, thiserror::Error, miette::Diagnostic)]
pub enum MyError<ErrType: 'static + Error = BoxedError> {
#[error("Error containing another error")]
AnError(#[source] ErrType),
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Chayim Friedman |
