I have a String
newtype ErrorMessage
that I'm using for errors in a prototype crate. (I know that this is a bad practice. I will construct a proper set of distinct error types before publication.)
I need ErrorMessage
to implement the Error
trait, which is (practically) empty but requires that it also implement the Display
and Debug
traits, which I have done.
pub struct ErrorMessage(pub String);
impl std::error::Error for ErrorMessage {}
impl std::fmt::Display for ErrorMessage {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
impl std::fmt::Debug for ErrorMessage {
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
self.0.fmt(f)
}
}
This works fine. However, I recently came across Deref
and was wondering if it could automatically delegate trait implementations to the implementations for String
from self.0
.
impl std::ops::Deref for ErrorMessage {
type Target = str;
fn deref(&self) -> &str {
&self.0
}
}
This allows me to call methods like .to_string()
on an ErrorMessage
, and deref coercion will let it use my Deref
implementation to automatically find the fmt
and to_string
implementations on self.0
/*self
.
However, ErrorMessage
itself isn't actually Display
or Debug
. If I try to println!
or format!
an instance directly I get an error, and it doesn't satisfy the bounds for Error
.
fn main() -> Result<(), ErrorMessage> {
Err(ErrorMessage("hello world".to_string()))
}
error[E0277]: `ErrorMessage` doesn't implement `std::fmt::Display`
--> src/main.rs:2:6
|
2 | impl std::error::Error for ErrorMessage {}
| ^^^^^^^^^^^^^^^^^ `ErrorMessage` cannot be formatted with the default formatter
|
= help: the trait `std::fmt::Display` is not implemented for `ErrorMessage`
Is there any way to use Deref
, DerefMut
, or something similar to allow dereferenced values to satisfy trait bounds for the original values. I'm looking for something automatic, as an alternative to manually writing impl
blocks to delegate each of them.
Implementing Deref for smart pointers makes accessing the data behind them convenient, which is why they implement Deref. On the other hand, the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers. Because of this, Deref should only be implemented for smart pointers to avoid confusion.
In mutable contexts, DerefMut is used. Implementing Deref for smart pointers makes accessing the data behind them convenient, which is why they implement Deref. On the other hand, the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers.
This mechanism is called ‘ Deref coercion’. In mutable contexts, DerefMut is used. Implementing Deref for smart pointers makes accessing the data behind them convenient, which is why they implement Deref. On the other hand, the rules regarding Deref and DerefMut were designed specifically to accommodate smart pointers.
If T implements Deref<Target = U>, and x is a value of type T, then: In immutable contexts, *x (where T is neither a reference nor a raw pointer) is equivalent to *Deref::deref (&x).
Is there any way to use
Deref
,DerefMut
, or something similar to allow dereferenced values to satisfy trait bounds for the original values.
No. An outer type that dereferences to an inner type does not itself implement the traits that the inner type does.
as an alternative to manually writing
impl
blocks to delegate each of them.
Your best bet may be to create one or more macros. I'm personally holding out hope for first-class delegation support.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With