๐Ÿฆ€ Functional Rust
๐ŸŽฌ Traits & Generics Shared behaviour, static vs dynamic dispatch, zero-cost polymorphism.
๐Ÿ“ Text version (for readers / accessibility)

โ€ข Traits define shared behaviour โ€” like interfaces but with default implementations

โ€ข Generics with trait bounds: fn process(item: T) โ€” monomorphized at compile time

โ€ข Static dispatch (impl Trait) = zero cost; dynamic dispatch (dyn Trait) = runtime flexibility via vtable

โ€ข Blanket implementations apply traits to all types matching a bound

โ€ข Associated types and supertraits enable complex type relationships

403: Display, Debug, and Formatting

Difficulty: 2 Level: Intermediate Two separate formatting traits for two audiences โ€” users and developers โ€” with format specifiers that call each one.

The Problem This Solves

Every type eventually needs to be printed. But "printing for users" and "printing for debugging" are different needs. A `Duration` displayed to a user should say "2 hours 15 minutes." Displayed to a developer it should show `Duration { secs: 8100, nanos: 0 }`. Conflating these creates user-facing output full of struct internals, or debug sessions where you can't see what's actually stored. Rust separates these concerns at the trait level. `Display` (invoked by `{}`) is for human-readable, user-facing output that you design deliberately. `Debug` (invoked by `{:?}`) is for developers โ€” show everything, make it inspectable. You implement `Display` manually when you care about the presentation. You derive `Debug` automatically in almost every case. A third need: zero-allocation formatting โ€” `format_args!` captures format arguments lazily without immediately allocating a `String`. This matters in hot paths, logging infrastructure, and embedded systems.

The Intuition

`{}` calls `Display` (write this for humans), `{:?}` calls `Debug` (write this for developers) โ€” two audiences, two traits, one type.

How It Works in Rust

use std::fmt;

struct Temperature { celsius: f64 }

// Display: user-facing, deliberate design
impl fmt::Display for Temperature {
 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     write!(f, "{:.1}ยฐC", self.celsius)
 }
}

// Debug: auto-derived for developer inspection
#[derive(Debug)]
struct Temperature { celsius: f64 }

// Usage
let t = Temperature { celsius: 23.5 };
println!("{}", t);    // "23.5ยฐC"     โ€” Display
println!("{:?}", t);  // "Temperature { celsius: 23.5 }" โ€” Debug
println!("{:#?}", t); // pretty-printed Debug

// Format specifiers
println!("{:>10}", t);  // right-align in 10 chars
println!("{:0>5}", 42); // "00042" โ€” zero-pad

// Custom Debug (when derived doesn't suit)
impl fmt::Debug for Temperature {
 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     f.debug_struct("Temperature")
         .field("celsius", &self.celsius)
         .field("fahrenheit", &(self.celsius * 1.8 + 32.0))
         .finish()
 }
}
1. Derive `Debug` on almost every type โ€” it's free and invaluable. 2. Implement `Display` when the type has a natural user-facing representation. 3. `write!(f, ...)` in the `fmt` method โ€” same syntax as `println!` but writes to a formatter.

What This Unlocks

Key Differences

ConceptOCamlRust
User-facing print`Printf.printf` / `Format.printf``impl Display` + `{}`
Debug/developer print`#show_type` or manual`#[derive(Debug)]` + `{:?}`
Format strings`%d`, `%s` type-checked at compile time`{}`, `{:?}` trait-dispatched
Custom formatters`Format.pp_print_*``impl fmt::Display` / `impl fmt::Debug`
Pretty print`Format` module with boxes`{:#?}` for pretty Debug, or custom `Display`
// Display, Debug, and formatting in Rust
use std::fmt;

#[derive(Debug, Clone, PartialEq)]
enum Color {
    Red,
    Green,
    Blue,
    Rgb(u8, u8, u8),
}

// Custom Display (user-facing)
impl fmt::Display for Color {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match self {
            Color::Red => write!(f, "red"),
            Color::Green => write!(f, "green"),
            Color::Blue => write!(f, "blue"),
            Color::Rgb(r, g, b) => write!(f, "rgb({},{},{})", r, g, b),
        }
    }
}

// Lower-hex formatter
impl fmt::LowerHex for Color {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let (r, g, b) = match self {
            Color::Red => (255, 0, 0),
            Color::Green => (0, 255, 0),
            Color::Blue => (0, 0, 255),
            Color::Rgb(r, g, b) => (*r as u32, *g as u32, *b as u32),
        };
        write!(f, "#{:02x}{:02x}{:02x}", r, g, b)
    }
}

#[derive(Debug, Clone)]
struct Point { x: f64, y: f64 }

impl fmt::Display for Point {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        // Respect precision from format string
        match f.precision() {
            Some(p) => write!(f, "({:.prec$}, {:.prec$})", self.x, self.y, prec = p),
            None => write!(f, "({:.2}, {:.2})", self.x, self.y),
        }
    }
}

struct Matrix([[f64; 2]; 2]);

impl fmt::Display for Matrix {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "[ {:.1} {:.1} ]
[ {:.1} {:.1} ]",
            self.0[0][0], self.0[0][1], self.0[1][0], self.0[1][1])
    }
}

fn main() {
    let colors = [Color::Red, Color::Green, Color::Rgb(128, 64, 32)];
    for c in &colors {
        println!("Display: {}  Debug: {:?}  Hex: {:x}", c, c, c);
    }

    let p = Point { x: 3.14159, y: 2.71828 };
    println!("Default: {}", p);
    println!("2 decimals: {:.2}", p);
    println!("4 decimals: {:.4}", p);
    println!("Debug: {:?}", p);
    println!("Pretty debug: {:#?}", p);

    let m = Matrix([[1.0, 2.0], [3.0, 4.0]]);
    println!("Matrix:
{}", m);

    // Format specifiers
    println!("{:>10}", "right");
    println!("{:<10}", "left");
    println!("{:^10}", "center");
    println!("{:0>5}", 42);
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_display() {
        assert_eq!(format!("{}", Color::Red), "red");
        assert_eq!(format!("{}", Color::Rgb(10, 20, 30)), "rgb(10,20,30)");
    }

    #[test]
    fn test_debug() {
        assert_eq!(format!("{:?}", Color::Blue), "Blue");
    }

    #[test]
    fn test_hex_format() {
        assert_eq!(format!("{:x}", Color::Red), "#ff0000");
    }

    #[test]
    fn test_point_precision() {
        let p = Point { x: 1.23456, y: 7.89012 };
        assert_eq!(format!("{:.1}", p), "(1.2, 7.9)");
    }
}
(* Display and Debug formatting in OCaml *)

type color = Red | Green | Blue | Rgb of int * int * int

let string_of_color = function
  | Red -> "red"
  | Green -> "green"
  | Blue -> "blue"
  | Rgb (r, g, b) -> Printf.sprintf "rgb(%d,%d,%d)" r g b

let debug_color = function
  | Red -> "Color::Red"
  | Green -> "Color::Green"
  | Blue -> "Color::Blue"
  | Rgb (r, g, b) -> Printf.sprintf "Color::Rgb(%d, %d, %d)" r g b

type point = { x: float; y: float }

let string_of_point {x; y} = Printf.sprintf "(%.2f, %.2f)" x y
let debug_point {x; y} = Printf.sprintf "Point { x: %g, y: %g }" x y

let () =
  let colors = [Red; Green; Rgb (128, 64, 32)] in
  List.iter (fun c ->
    Printf.printf "display: %s  debug: %s\n"
      (string_of_color c) (debug_color c)
  ) colors;
  let p = {x = 3.14; y = 2.72} in
  Printf.printf "display: %s  debug: %s\n" (string_of_point p) (debug_point p)