Rust formatting reference
Rust formatting has one small language inside another. Most confusion comes from mixing three separate questions:
- Which value is being formatted?
- Which formatting trait should represent it?
- Where should the resulting text go?
This page is the map. The linked pages carry the checked examples and edge cases.
The replacement field in one table
Section titled “The replacement field in one table”| Pattern | Meaning | Read next |
|---|---|---|
{} |
use Display |
Debug vs Display |
{:?} |
use Debug |
inspect values with Debug |
{:#?} |
pretty Debug |
pretty Debug |
{:>8} |
minimum width 8, right aligned | width, alignment, and fill |
{:.2} |
precision 2 | precision |
{:#x} |
lower hex with alternate prefix | alternate formatting |
{:08} |
numeric zero padding | signs and zero padding |
{{ and }} |
literal braces | escaping braces |
The full grammar is:
{ [argument] [ : format_spec ] }and the format specification combines fill, alignment, sign, alternate form, zero padding, width, precision, and a formatting type. See the format specifier grammar when you need the exact order.
Start with the trait, not the punctuation
Section titled “Start with the trait, not the punctuation”An empty formatting type uses Display. A question mark uses Debug. Numeric format types select traits such as hexadecimal, binary, octal, exponential notation, or pointer formatting.
That matters because formatting is trait-driven. If a value does not implement the requested trait, changing width or precision cannot fix the problem.
Use:
Displayfor intentional user-facing textDebugfor programmer-facing inspection- numeric formatting traits when the representation itself is numeric
The formatting traits reference maps the syntax to the underlying traits.
Width is a minimum
Section titled “Width is a minimum”Width does not truncate a long value. It asks the formatter to occupy at least that many columns and to fill extra space when the output is shorter.
Alignment controls where the value sits inside that width:
<left^center>right
A fill character can appear immediately before the alignment operator. This is why {:.^8} means “center within width 8 and fill with dots.”
For strings and table-like output, start with width, alignment, and fill.
Precision is type-dependent
Section titled “Precision is type-dependent”Precision is not one universal “number of characters” switch.
For floating-point output it controls digits after the decimal point in the common fixed-point case. For strings it can limit the displayed content. Other formatting traits interpret it according to their own rules.
Use the dedicated precision guide instead of assuming that {:.2} means the same thing for every type.
Numeric flags interact
Section titled “Numeric flags interact”Numeric formatting has combinations that look simple until signs and prefixes appear.
The alternate flag # can request prefixes such as 0x for hexadecimal. Zero padding is sign-aware and prefix-aware. A pattern such as {:#08x} is therefore not equivalent to manually filling a string with zeroes.
Use alternate formatting together with signs and zero padding for numeric output.
Arguments can be positional, named, or captured
Section titled “Arguments can be positional, named, or captured”Modern Rust format strings can capture variables from the surrounding scope. Width and precision can also come from arguments.
That gives you patterns such as:
{value:>width$}The format string itself still has to be a literal so the compiler can parse and validate it. If the format must be assembled dynamically at runtime, the normal formatting macros are not a runtime template engine.
See arguments and capture and runtime format strings for the boundary.
Formatting and destination are different decisions
Section titled “Formatting and destination are different decisions”The formatting language describes the representation. The macro decides where the formatted output goes.
| Goal | Typical macro |
|---|---|
| stdout with newline | println! |
| stdout without newline | print! |
| stderr | eprint! / eprintln! |
new String |
format! |
| existing writer or buffer | write! / writeln! |
| prebuilt formatting arguments | format_args! |
The output macro map explains these destinations, and the Output Macro Chooser can select one from the intended destination and newline behavior.
Debug output is not serialization
Section titled “Debug output is not serialization”Debug exists for programmer-facing inspection. Its representation is not a wire format, persistence format, or compatibility contract.
If another program has to parse the output later, use a real serialization format with an explicit schema or compatibility policy.
The same warning applies to pretty Debug. It is for humans reading diagnostics, not for storing durable data.
The fast path
Section titled “The fast path”If you have a concrete formatting string and want to decode it, open the Rust Format Explorer.
If you only need syntax at a glance, use the formatting cheat sheet.
If the compiler rejects the expression, start in formatting errors or paste the diagnostic into the Formatting Error Explainer.