Step 1 of 5 · Reading · ~3 min
Learn
Strings
Formatting
By the end of this lesson you can control exactly how a value is printed: how many decimals, how wide the column is, which side it hugs, and what fills the gap. Every exercise in this course is graded byte for byte, so this is the skill that turns "looks right" into "passes".
Placeholders and the format spec
A {} in println! is a placeholder: it is replaced by the next argument, using the type's Display rendering (the human-facing one). Anything you put after a colon inside the braces is the format spec, a small language that says how to render that one value:
let city = "Oslo";
let temp = 3.14159;
println!("{} is {}", city, temp); // Oslo is 3.14159
println!("{} is {:.1}", city, temp); // Oslo is 3.1
The format string is checked at compile time: one {} too many or too few is a build error, not garbled output. Named placeholders such as {city} that pull a variable from scope need Rust 1.58+, and this course's grader is older, so always pass values as arguments. For collections, {:?} uses Debug formatting instead: println!("{:?}", vec![1, 2]) prints [1, 2].
Precision: {:.N}
.N means "exactly N digits after the decimal point". It rounds to the nearest value and it adds zeros when the number has fewer digits:
println!("{:.2}", 9.5); // 9.50 (zeros added)
println!("{:.2}", 0.4567); // 0.46 (rounded up, not cut to 0.45)
println!("{:.1}", 7.96); // 8.0 (rounding can carry into the integer part)
println!("{}", 9.5); // 9.5 (plain {} never pads with zeros)
Width, alignment and fill
A number before the precision is the width: the minimum number of characters the value occupies. Shorter values are padded with spaces; longer values are printed in full, never cut. The alignment character goes before the width: < left, > right, ^ center.
The default side depends on the type, and this is the trap: strings default to left, numbers default to right.
println!("|{:6}|", "tea"); // |tea | string: left by default
println!("|{:6}|", 42); // | 42| number: right by default
println!("|{:>6}|", "tea"); // | tea| forced right
println!("|{:^7}|", "tea"); // | tea | centered
println!("|{:4}|", "espresso"); // |espresso| too long: printed whole
A 0 right before the width pads a number with leading zeros instead of spaces:
println!("#{:05}", 318); // #00318
println!("#{:5}", 318); // # 318 same width, spaces
println!("#{:05}", 123456); // #123456 already wider than 5: untouched
Putting specs together
Here is a small receipt line that uses all three on different values:
let item = "mug";
let qty = 4;
let unit = 3.5;
println!("{:<8}x{:>3} = {:>7.2}", item, qty, unit * qty as f64);
// mug x 4 = 14.00
{:>7.2} reads as: right-aligned, width 7, two decimals. The order inside the braces is always alignment, then 0, then width, then .precision. The same spec language works in print!, format! (which returns a String instead of printing) and eprintln! (which writes to stderr, so debug output there never breaks graded stdout).
Common mistakes
- Plain
{}on a float.1.5prints as1.5, not1.50; only a precision like{:.2}adds the missing zero. - Width without the
0.{:3}on7prints7(two spaces), not007. The zero flag is what makes the padding digits. - Forcing the wrong side on a string.
{:>8}right-aligns, soabbecomesab; a text column that should hug the left needs<or no alignment character at all. - Truncating instead of rounding. Converting to an integer to cut decimals turns
2.678into2.67;{:.2}gives the correct2.68.
Your exercise
Print a three-line name badge using width, zero padding and precision. The starter reads the name, age and height and prints them with bare {}, so every line is wrong until you add the right format specs. Watch the padded name column and the rounding of the height.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…