๐Ÿฆ€ Functional Rust

474: format!, write!, writeln!

Difficulty: 1 Level: Beginner Rust's compile-time-checked formatting macros โ€” Python's f-strings, but verified at compile time.

The Problem This Solves

In Python, f-strings and `.format()` are evaluated at runtime. A typo in a format string (`f"{naem}"`) only fails when that line executes. In JavaScript, template literals offer no type checking. You can pass anything and get a surprise. Rust's format macros are checked at compile time. If you write `format!("{} {}", x)` but only provide one argument, it's a compile error โ€” not a runtime crash. If you use `{:?}` on a type that doesn't implement `Debug`, it won't compile. This catches entire classes of bugs before your code ships. Beyond correctness, Rust's format specifiers are comprehensive: alignment, padding, precision, radix (hex, binary, octal), scientific notation, named arguments, and debug printing. And with `write!`/`writeln!`, you can format directly into any `Write` target โ€” a `String`, a file, a network socket โ€” without intermediate allocations.

The Intuition

`format!()` is Python's f-strings. The `{}` placeholder calls `Display` (the user-facing representation). `{:?}` calls `Debug` (the developer representation โ€” roughly `repr()` in Python). `{:#?}` is pretty-printed debug. Think of format specifiers as a mini-language inside the braces: `write!(buf, ...)` writes into a `String` or `File` โ€” same syntax, no allocation overhead.

How It Works in Rust

use std::fmt::Write as FmtWrite;  // needed for write! on String

// Basic substitution
let s = format!("Hello, {}! Age: {}", "Alice", 30);

// Alignment: < left, > right, ^ center
println!("|{:<10}|{:>10}|", "left", "right"); // |left      |     right|
println!("|{:^10}|", "center");               // |  center  |

// Zero-padding: fill char before >
println!("{:0>8}", 42);   // 00000042
println!("{:08}", 42);    // 00000042 (shorthand for numbers)

// Number radix
println!("{:x}", 255);    // ff
println!("{:X}", 255);    // FF
println!("{:b}", 42);     // 101010
println!("{:o}", 8);      // 10

// Floating point precision
println!("{:.2}", 3.14159);    // 3.14
println!("{:e}", 1_234_567.0); // 1.234567e6

// Debug vs Display
println!("{:?}", vec![1,2,3]);  // [1, 2, 3]  โ† Debug
println!("{:#?}", vec![1,2,3]); // pretty-printed

// Write into a String buffer (no allocation for the format itself)
let mut buf = String::new();
write!(buf, "x={} ", 42).unwrap();
writeln!(buf, "y={}", 99).unwrap();

// Named arguments (Rust 1.58+)
let name = "Bob";
let age = 25u32;
println!("{name} is {age}");  // same as "{} is {}", name, age

What This Unlocks

Key Differences

ConceptOCamlRust
String interpolation`Printf.sprintf "%s! %d" str int``format!("{} {}", str, int)`
Left-align`%-10s``{:<10}`
Right-align`%10s``{:>10}`
Zero-pad number`%08d``{:08}`
Float precision`%.2f``{:.2}`
Hex`%x``{:x}`
Write to buffer`Printf.bprintf buf``write!(buf, ...)`
Debug printNo built-in`{:?}` via `Debug` trait
Format checked atRuntimeCompile time
// 474. format!, write!, writeln!
use std::fmt::Write as FmtWrite;

fn main() {
    let s = format!("Hello, {}! Age: {}", "Alice", 30);
    println!("{}", s);

    // Alignment and padding
    println!("|{:<10}|{:>10}|", "left", "right");
    println!("|{:^10}|", "center");
    println!("|{:0>8}|", 42);

    // Number formats
    println!("hex={:x} HEX={:X} bin={:08b} oct={:o}", 255u8, 255u8, 42u8, 8u8);
    println!("sci={:e} pi={:.5}", 1_234_567.89f64, std::f64::consts::PI);

    // Debug vs Display
    println!("{:?}",  vec![1,2,3]);
    println!("{:#?}", vec![1,2,3]);

    // Write to String buffer
    let mut buf = String::new();
    write!(buf, "x={} ", 42).unwrap();
    writeln!(buf, "y={}", 99).unwrap();
    print!("buf: {}", buf);

    // Named capture (Rust 1.58+)
    let name="Bob"; let age=25u32;
    println!("{name} is {age}");
}

#[cfg(test)]
mod tests {
    use std::fmt::Write;
    #[test] fn test_align()  { assert_eq!(format!("{:>5}","hi"),"   hi"); assert_eq!(format!("{:<5}","hi"),"hi   "); }
    #[test] fn test_nums()   { assert_eq!(format!("{:x}",255u8),"ff"); assert_eq!(format!("{:.2}",3.14159f64),"3.14"); }
    #[test] fn test_write()  { let mut s=String::new(); write!(s,"{}",42).unwrap(); assert_eq!(s,"42"); }
    #[test] fn test_debug()  { assert_eq!(format!("{:?}",vec![1,2,3]),"[1, 2, 3]"); }
}
(* 474. String formatting โ€“ OCaml *)
let () =
  let s = Printf.sprintf "Hello, %s! Age: %d" "Alice" 30 in
  print_string s; print_newline ();
  Printf.printf "|%-10s|%10s|\n" "left" "right";
  Printf.printf "|%010d|\n" 42;
  Printf.printf "pi=%.5f sci=%e\n" Float.pi 1234567.89;
  let buf = Buffer.create 32 in
  Buffer.add_string buf "start";
  Printf.bprintf buf " %d" 99;
  Printf.printf "buf=%s\n" (Buffer.contents buf)