Implementing Display
🎯 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:
| Trait | Format specifier | Purpose | Python 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.
Topics
This is a premium exercise
Log in to unlock the full exercise and start coding.
Login to access this exercise