Formatting
Use format!, write!, and std::fmt traits with precision, width, alignment, and custom Display/Debug.
Recipe
fn main() {
let pi = 3.14159;
println!("{pi:.2}");
let s = format!("{:>8}", "right");
println!("{s}");
}When to reach for this: User-facing strings, logs, tables, padded output.
Working Example
use std::fmt;
struct Percent(f64);
impl fmt::Display for Percent {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{:.1}%", self.0 * 100.0)
}
}
fn main() {
println!("{}", Percent(0.856));
}What this demonstrates:
- Custom
Displayfor domain formatting - Formatter precision control
write!macro inside impl
Deep Dive
Specifiers: hex, binary, {:?} Debug, {:#?} pretty Debug, {name} named args, {:08} pad zeros.
Gotchas
- Debug for users - Ugly escapes. Fix:
Display. - format in hot loop - Alloc each time. Fix: reuse buffer
write!. - Wrong precision on integers - Still works - width vs precision differs.
- Display not derivable - Manual impl required.
- Unicode width vs chars - Padding uses char width mostly - terminal nuances.
Alternatives
| Alternative | Use When | Don't Use When |
|---|---|---|
itertools::format | Join iter | Simple format! |
| templating crates | HTML/email | Small CLI message |
serde_json::to_string | JSON not human | User text |
FAQs
align syntax?
<, >, ^ for left right center.fill char?
{:*>10} pad asterisks.debug_struct?
fmt::DebugStruct builder.Pointer fmt?
{:p} address.Binary octal?
{:b} {:o}.scientific?
{:e} floats.locales?
Not in std.writeln?
Newline append.to_string Display?
format!("{v}") if Display.tracing fields?
% for Display ? for Debug.Related
Stack versions: This page was written for Rust 1.97.0 (edition 2024), Tokio 1.x, Axum 0.8, serde 1.0, sqlx 0.8, clap 4, and Polars 0.46+.