5

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Jeremy
  • 1
  • 85
  • 340
  • 366
  • Note that your code does work if you fix the deref implementation (see https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=987a22ee51938deac9c917aa6907b29d) – aochagavia Mar 07 '19 at 08:57
  • you can derive Debug for ErrorMessage, but Display should still be implemented manually – Laney Mar 07 '19 at 10:29
  • @aochagavia Thanks for the correction! I've fixed the deref definition and updated my sandbox examples. Unfortunately that still doesn't let me get rid of the `impl Debug` and `impl Display` blocks. – Jeremy Mar 07 '19 at 17:56
  • You cannot inherit trait implementations, but for common traits you can use the [derive_more](https://jeltef.github.io/derive_more/derive_more/) crate. Does this answer your question? If yes, I can post it as an answer. – aochagavia Mar 12 '19 at 10:14
  • @aochagavia Thanks for the suggestion, that's good to know! but it's not quite what I'm looking for. – Jeremy Mar 16 '19 at 22:50

1 Answers1

8

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.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366