Display vs Debug
`Display` is for human-readable output (`{}` in format strings) — implement it for types whose textual form is meaningful to end users. `Debug` is for developer-facing output (`{:?}`) — implement it on every public type, usually with `#[derive(Debug)]`. Display has no derive; you must implement it explicitly because the right human-readable form is type-specific.
Formatter and write!
Both Display and Debug receive a `&mut Formatter` and return `fmt::Result`. Inside the impl, use the `write!` macro with the Formatter as its target. The Formatter exposes width, precision, fill character, and alignment info via methods like `f.width()`, `f.precision()`, and `f.align()` — implement these to support format-specifier flags like `{:>10.3}`.
For deeper background, see a complete cheat-sheet of std collection complexities for the broader context behind this section.
Format spec mini-language
Format specs follow the syntax `{[arg][:[fill][align][sign][#][0][width][.precision][type]]}`. The `type` is one of `?` (Debug), `x` / `X` (hex), `o` (octal), `b` (binary), `e` / `E` (scientific). `#` adds a prefix (`0x`, `0o`, `0b`). `0` enables zero-padding. Named arguments like `{name}` use captured locals or explicit named args.
Custom format types
Implement `LowerHex`, `UpperHex`, `Binary`, `Octal`, `LowerExp`, `UpperExp` to respond to the corresponding format specifier. Each has the same shape as Display — they exist purely so the spec language can dispatch to the right impl.
For deeper background, see the official Rust API guidelines for module-level design for the broader context behind this section.
Performance: writeln! and write_str
Each `format!` call allocates a fresh String. For high-throughput formatting, build a `String` once with `String::with_capacity` and call `write!` (the macro) into it repeatedly — no repeated allocation. For pure I/O, write straight into a `BufWriter` to amortise syscalls and skip the intermediate String.