Reputation: 22601
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>
.
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.
(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),
}
Upvotes: 1
Views: 616
Reputation: 71025
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),
}
Upvotes: 3