Essential Standard Traits

Display

The std::fmt::Display trait formats a value for user-facing text output using the {} format specifier and cannot be automatically derived with #[derive(Display)].

Trait Definition

pub trait Display {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
}

Unlike Debug, which targets developers and has a canonical structure for any type, Display defines how a value appears to end users in logs, CLI output, or error messages. Because user-facing representation is a design decision, Display must always be implemented manually.

Implementing Display

Implement fmt using the write! macro to write formatted text into the Formatter buffer f:

use std::fmt;

struct ByteSize(u64);

impl fmt::Display for ByteSize {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        const KB: u64 = 1024;
        const MB: u64 = 1024 * KB;

        if self.0 >= MB {
            write!(f, "{:.2} MiB", self.0 as f64 / MB as f64)
        } else if self.0 >= KB {
            write!(f, "{:.2} KiB", self.0 as f64 / KB as f64)
        } else {
            write!(f, "{} B", self.0)
        }
    }
}

fn main() {
    println!("{}", ByteSize(512));
    println!("{}", ByteSize(4096));
    println!("{}", ByteSize(5_242_880));
}
512 B
4.00 KiB
5.00 MiB

Automatic ToString Blanket Implementation

The standard library provides a blanket implementation of ToString for every type that implements Display:

impl<T: fmt::Display + ?Sized> ToString for T {
    // Provides .to_string()
}

Once a type implements Display, calling .to_string() on it works automatically without a separate impl ToString:

use std::fmt;

struct Coordinate {
    lat: f64,
    lon: f64,
}

impl fmt::Display for Coordinate {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "({:.4}, {:.4})", self.lat, self.lon)
    }
}

fn main() {
    let loc = Coordinate {
        lat: 23.6238,
        lon: 90.5000,
    };
    let s: String = loc.to_string();
    println!("Formatted string: {s}");
}
Formatted string: (23.6238, 90.5000)

Display vs. Debug

Aspect Display ({}) Debug ({:?})
Intended audience End users, CLI output, error messages Developers, logs, test assertions
Derivable via #[derive] No — must be written manually Yes (#[derive(Debug)])
Blanket ToString impl Yes — provides .to_string() No
Standard containers (Vec<T>, Option<T>) Do not implement Display Implement Debug when T: Debug

Rules for Display::fmt

  • Never panic inside fmt. If formatting fails, propagate the fmt::Error returned by write! using ?.
  • Do not allocate an intermediate String via format! only to pass it to f.write_str(...); write directly to f with write!(f, ...).