Pybites Logo Rust Platform

Implementing Display

Easy +2 pts

🎯 In Python, __str__ controls what users see when they print an object:

class Fraction:
    def __init__(self, num, den):
        self.num, self.den = num, den

    def __str__(self):
        return f"{self.num}/{self.den}"

print(Fraction(3, 4))  # 3/4

And __repr__ is for developers — what you see in the REPL or in debug output. Many Python developers conflate the two or only implement one.

Rust makes this separation explicit with two traits:

TraitFormat specifierPurposePython equivalent
Debug{:?}Developer output__repr__
Display{}User-facing output__str__

You met Debug in the previous exercise — it can be derived with #[derive(Debug)]. Display is different: it cannot be derived. The compiler won't guess what user-facing output should look like, so you always implement it manually.

Implementing Display

The signature is identical to Debug — you implement fmt on the fmt::Display trait:

use std::fmt;

struct Fraction {
    num: i32,
    den: i32,
}

impl fmt::Display for Fraction {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}/{}", self.num, self.den)
    }
}

println!("{}", Fraction { num: 3, den: 4 });  // 3/4

The write! macro writes into the formatter buffer, just like with Debug. The only difference is which format specifier triggers it: {} calls Display, {:?} calls Debug.

The free ToString

Here's a bonus Python developers appreciate: implementing Display automatically gives you .to_string(). Rust has a blanket implementation:

// This exists in the standard library:
impl<T: Display> ToString for T {
    fn to_string(&self) -> String {
        // uses Display::fmt internally
    }
}

So once you implement Display, this works:

let f = Fraction { num: 3, den: 4 };
let s: String = f.to_string();  // "3/4"

In Python, str(obj) calls __str__. In Rust, .to_string() calls Display::fmt. Same concept, different syntax.

Debug + Display together

Most types should have both. A common pattern:

#[derive(Debug)]  // derive Debug for {:?}
struct Fraction { num: i32, den: i32 }

impl fmt::Display for Fraction {  // manually implement Display for {}
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{}/{}", self.num, self.den)
    }
}

Debug output: Fraction { num: 3, den: 4 } (verbose, shows field names — good for debugging). Display output: 3/4 (clean, user-facing).

Python's @dataclass generates __repr__ but not __str__. Rust's #[derive(Debug)] generates Debug but not Display. In both languages, user-facing representation requires intentional design.

Login to see the full task and start coding.

This is a premium exercise

Log in to unlock the full exercise and start coding.

Login to access this exercise