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.
Recipe
#[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.
Working Example
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:
- Manual
Displayimpl for custom formatting - Alignment width
{:>10}right-aligns in 10 columns format!allocates aStringeprintln!writes to stderr
Deep Dive
Common Macros
| Macro | Output | Returns |
|---|---|---|
println! | stdout + newline | () |
print! | stdout | () |
eprintln! | stderr + newline | () |
format! | none | String |
write! / writeln! | any fmt::Write | Result |
Format Specifiers
println!("{:.2}", 3.14159); // 3.14
println!("{:08x}", 255); // 000000ff
println!("{name}", name = "Rust"); // named argsDebug vs Display
Debug: developer-oriented, often derivedDisplay: user-facing, manual impl required for custom types#?pretty-printsDebugwith indentation
Gotchas
println!(my_struct)withoutDisplay- Compile error. Fix: DeriveDebugand use{:?}, or implDisplay.- Formatting non-UTF-8 for users -
Debugmay escape strings oddly. Fix: ImplementDisplayfor human text. - Using
format!in hot loops - Allocates every call. Fix: Usewrite!to a buffer or reuseStringwithclear(). - Wrong brace escaping -
{{and}}produce literal braces. Fix: Double braces in format strings. - Panicking in
Display::fmt- Should returnfmt::Error, not panic. Fix: Propagatefmt::Result.
Alternatives
| 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 |
FAQs
Can I implement both `Display` and `Debug`?
Yes. They serve different audiences. Many types derive Debug and manually impl Display.
How do I print without newline?
Use print! instead of println!.
What is `Formatter` padding API?
f.pad_integral, f.precision(), f.width() read specifiers inside fmt impls.
Can formatting fail?
write! returns Result. println! panics on I/O error (rare for stdout).
How do I print `Option` nicely?
{:?} works. For custom: match and format inner value.
Does `Display` imply `ToString`?
Blanket impl exists: types with Display get to_string().
Can I use ANSI colors?
Not in std. Crates like owo-colors wrap Display output.
What about `dbg!` macro?
Prints file/line and expression value to stderr. Handy for quick debugging.
How do I format iterators?
itertools::format or collect to Vec then join for separators.
Locale-aware number formatting?
Std has no i18n. Use external crates for locale rules.
Related
- Strings Basics -
Stringvs&str - Formatting - advanced format patterns
- Deriving Traits -
#[derive(Debug)] - Common Standard Traits -
Display,Debug
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+.