Formatting & Printing
Rust formats text with macros (println!, format!, write!) backed by the std::fmt traits Display and Debug. Format specifiers control width, precision, and alignment.
Search across all documentation pages
Rust formats text with macros (println!, format!, write!) backed by the std::fmt traits Display and Debug. Format specifiers control width, precision, and alignment.
#[derive(Debug)]
struct User { id: u64, name: String }
fn main() {
let u = User { id: 1, name: "Ada".into() };
println!("{}", u.name);
println!("{:?}", u);
println!("{:#?}", u);
eprintln!("log: id={}", u.id);
}When to reach for this: CLI output, logs, test failures, and implementing user-visible string forms of your types.
use std::fmt;
struct Point { x: i32, y: i32 }
impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
fn main() {
let p = Point { x: 3, y: 4 };
println!("point={p}");
println!("{:>10}", p);
let s = format!("moved {p}");
assert_eq!(s, "moved (3, 4)");
}What this demonstrates:
Display impl for custom formatting{:>10} right-aligns in 10 columnsformat! allocates a Stringeprintln! writes to stderr| Macro | Output | Returns |
|---|---|---|
println! | stdout + newline | () |
print! | stdout | () |
eprintln! | stderr + newline | () |
format! | none | String |
write! / writeln! | any fmt::Write | Result |
println!("{:.2}", 3.14159); // 3.14
println!("{:08x}", 255); // 000000ff
println!("{name}", name = "Rust"); // named argsDebug vs DisplayDebug: developer-oriented, often derivedDisplay: user-facing, manual impl required for custom types#? pretty-prints Debug with indentationprintln!(my_struct) without Display - Compile error. Fix: Derive Debug and use {:?}, or impl Display.Debug may escape strings oddly. Fix: Implement Display for human text.format! in hot loops - Allocates every call. Fix: Use write! to a buffer or reuse String with clear().{{ and }} produce literal braces. Fix: Double braces in format strings.Display::fmt - Should return fmt::Error, not panic. Fix: Propagate fmt::Result.| Alternative | Use When | Don't Use When |
|---|---|---|
tracing / log crates | Structured production logging | Quick scripts |
serde_json::to_string | Machine-readable output | Human CLI tables |
std::io::Write directly | Binary protocols | Text formatting |
to_string() on primitives | Simple scalar conversion | Custom layout rules |
Yes. They serve different audiences. Many types derive Debug and manually impl Display.
Use print! instead of println!.
f.pad_integral, f.precision(), f.width() read specifiers inside fmt impls.
write! returns Result. println! panics on I/O error (rare for stdout).
{:?} works. For custom: match and format inner value.
Blanket impl exists: types with Display get to_string().
Not in std. Crates like owo-colors wrap Display output.
Prints file/line and expression value to stderr. Handy for quick debugging.
itertools::format or collect to Vec then join for separators.
Std has no i18n. Use external crates for locale rules.
String vs &str#[derive(Debug)]Display, DebugStack 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+.
Reviewed by Chris St. John·Last updated Jul 19, 2026