Phil-ZXX
Phil-ZXX

Reputation: 3265

Define fmt::Display and fmt::Debug together

Is it possible to define fmt::Display and fmt::Debug together, i.e. such that they use the same implementation?

Assume we have the below sample code (purely for demonstration purposes):

use std::fmt;

struct MyDate {
    pub year: u16,
    pub month: u8,
    pub day: u8
}

impl PartialEq for MyDate {
    fn eq(&self, other: &Self) -> bool {
        self.year == other.year && self.month == other.month && self.day == other.day
    }
}

impl fmt::Display for MyDate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}-{:02}-{:02}", self.year, self.month, self.day)
    }
}

impl fmt::Debug for MyDate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self)
    }
}

fn main() {
    let d1 = MyDate { year: 2008, month: 9, day: 10 };
    let d2 = MyDate { year: 2008, month: 9, day: 10 };

    println!("Date = {}", d1);  // requires fmt::Display
    assert_eq!(d1, d2);         // requires fmt::Debug
}

It seems that in order to print my class using println and write unit tests using assert_eq, I need to define both fmt traits. Is there a way to have 1 "print" implementation that simultaneously defines fmt::Display and fmt::Debug? For example something along the lines of impl fmt::Debug, fmt::Display for MyDate { ... } or similar?

Or is it common practice to just put #[derive(Debug)] in front of any class? Apologies if my question is naïve, as I am new to rust.

Upvotes: 7

Views: 4133

Answers (2)

rodrigocfd
rodrigocfd

Reputation: 8052

Performance consideration: in your Debug implementation, you are calling the Display implementation of the same object:

impl fmt::Debug for MyDate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self) // call method from Display
    }
}

However, when doing this, Debug::fmt() calls write! twice – and this is not a cheap operation.

A better implementation would be calling directly the method from the Display trait:

impl fmt::Debug for MyDate {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        fmt::Display::fmt(self, f)
    }
}

Now write! is called only once.

Or is it common practice to just put #[derive(Debug)] in front of any class?

Yes, it's very common. Implementing your custom Debug is interesting only if you want a custom output of the fields in your struct.

Upvotes: 0

Kevin Reid
Kevin Reid

Reputation: 43782

In most cases, Display and Debug have different output. When it makes sense to use the same formatting in both cases, the code you've written is fine. It would also be fine to #[derive(Debug)] — it's your decision to make. I think the important thing to keep in mind is that Debug should not leave anything out, and be unambiguous.

In the case you show, the Display format shows all of the struct's fields completely accurately, so it's perfectly fine to reuse Display to implement Debug. If you had a lot of types like this, you could use a simple macro to generate Debug implementations that forward to Display.

By the way, I would suggest using #[derive(PartialEq)] instead of the manual PartialEq implementation you show. It's completely equivalent, and avoids the dangerous failure mode of forgetting to compare a newly added field.

Upvotes: 5

Related Questions