🎓 A1Learner
🔍

Rust Display Trait

Display is the trait that gives a type a tidy, user-facing text form. The {} placeholder prints a value through Display.

The {} placeholder asks for Display

Once a type has Display, {} prints it in its tidy, user-facing form:

let price = Money { dollars: 12, cents: 5 };
println!("{price}");

Write the impl by hand

Display is never derived — you write the one fmt method yourself:

impl fmt::Display for Money {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "${}.{:02}", self.dollars, self.cents)
    }
}

Several pieces: stop at the first error

End each earlier write! with ? — if one write fails, the method stops and returns that error at once:

write!(f, "[")?;
write!(f, "{}", self.name)?;
write!(f, "]")

Let the caller's width work

A row of plain write! calls ignores a caller's {:>7} — the value prints unpadded, as [#new]. Build the text first, then hand it to f.pad, and the width, fill and alignment all apply:

impl fmt::Display for Tag {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let text = format!("#{}", self.0);
        f.pad(&text)
    }
}
// println!("[{:>7}]", tag) with Tag("new"):

Display hands you to_string

You never write ToString yourself — Display brings it along, building the same tidy text as a String:

let text: String = price.to_string();
println!("{text}");

Example

use std::fmt;
struct Money { dollars: u32, cents: u32 }
impl fmt::Display for Money {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "${}.{:02}", self.dollars, self.cents)
    }
}
fn main() {
    let price = Money { dollars: 12, cents: 5 };
    println!("price = {}", price);
    println!("as text = {}", price.to_string());
}

Key points

  • Display is never derived: you write impl fmt::Display for YourType by hand, because Rust cannot guess the right look
  • The last write! in fmt is the return value, because fmt must hand back a fmt::Result that says whether writing worked
  • Vec<T> and other generic containers have no Display at all: print them with {:?}, or wrap them in your own type and give the wrapper a Display
  • The formatter reports what was asked: f.width(), f.precision(), f.alternate() and f.sign_plus() let one impl answer {:.2}, {:#} and {:+} differently instead of ignoring them
  • Debug is the programmer-facing form, printed with {:?}.
  • A price can print as $12.05 through Display.
  • Display gives a type the text form its users see.

In this example, Money holds dollars and cents as two separate numbers. The Display impl writes a dollar sign, the dollars, and a dot. It writes the cents padded to two digits with {:02}. So 5 cents shows as 05, not 5. main can now print price with {}, giving $12.05. The second line calls .to_string(), which Display provides for free, and it builds exactly the same text.

Note

Writing Display gives you ToString for free: every Display type gains .to_string(), which builds the same tidy text as a String.

Try it Yourself

Edit the code, then press Run.

Running is simulated · Rust Playground

Exercise

You write a Display impl for one of your types. Which extra power does the type get for free?