๐Ÿฆ€ Functional Rust

438: format_args! for Zero-Alloc Formatting

Difficulty: 3 Level: Advanced Capture format arguments lazily without allocating a `String` โ€” the low-level primitive that `println!`, `format!`, and logging frameworks build on.

The Problem This Solves

`format!("{} = {}", key, value)` always allocates a `String`, even if you're just going to write it to a buffer immediately. In hot loops, logging infrastructure, or embedded systems where the heap is precious, this allocation is waste. You want to describe what to format without paying the allocation cost until the bytes are actually written somewhere. `format_args!` is the compile-time macro that captures format arguments as a `fmt::Arguments` struct โ€” a lazy description of the formatting operation. No allocation happens. You pass `fmt::Arguments` to any `Write` implementor (`File`, `TcpStream`, your custom buffer) and it formats directly into the destination. `println!`, `format!`, `write!`, and `log!` all use `format_args!` internally. This matters when building logging, tracing, or I/O infrastructure. Accept `fmt::Arguments` in your API and callers get zero-alloc formatting for free.

The Intuition

`format_args!` captures a format expression as a value that describes the formatting to do, without doing it โ€” allocation happens only when you write it to a concrete output.

How It Works in Rust

use std::fmt;
use std::fmt::Write;

// format_args! returns fmt::Arguments โ€” no allocation
let args = format_args!("{} + {} = {}", 1, 2, 3);
// Nothing allocated yet โ€” args is a stack value

// Write to a String (allocates only here)
let mut s = String::new();
write!(s, "{}", args).unwrap();  // "1 + 2 = 3"

// Write to stderr directly โ€” no intermediate String
eprintln!("{}", args);  // format_args! inside

// Accept fmt::Arguments in your own API โ€” zero-alloc logging
fn log(level: &str, args: fmt::Arguments<'_>) {
 // Could write to file, buffer, network โ€” whatever
 println!("[{}] {}", level, args);
}

// Macro wrapper so callers use familiar syntax
macro_rules! my_log {
 ($level:expr, $($arg:tt)*) => {
     log($level, format_args!($($arg)*))
 };
}

my_log!("INFO", "user {} logged in from {}", user_id, ip);
// โ†‘ No String allocation โ€” format_args captures the args, log() writes them

// format_args! in a struct for deferred formatting
struct Lazy<'a>(fmt::Arguments<'a>);

impl<'a> fmt::Display for Lazy<'a> {
 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
     f.write_fmt(self.0)
 }
}
1. `format_args!(...)` โ€” same syntax as `format!` but returns `fmt::Arguments` instead of `String`. 2. `fmt::Arguments` is a stack value referencing the original arguments โ€” no heap allocation. 3. Pass to `write!`, `writeln!`, `print!`, or any `fmt::Write`/`io::Write` implementor. 4. Build macro wrappers: accept `$($arg:tt)` and forward to `format_args!($($arg))`.

What This Unlocks

Key Differences

ConceptOCamlRust
Format without allocation`Format.fprintf` with `formatter``format_args!` + `fmt::Write`
Deferred formatting`Format.kdprintf` continuation-passing`fmt::Arguments` value โ€” pass around
Format to sink`Format.fprintf out_channel``write!(sink, "{}", args)`
Building format macros`Format.kasprintf``macro_rules!` + `format_args!($($arg)*)`
String allocation`Format.asprintf``format!` (allocates); `format_args!` (doesn't)
// format_args! for zero-alloc formatting in Rust
use std::fmt::{self, Write};

// Custom Write target (stack-allocated buffer)
struct StackBuf {
    buf: [u8; 256],
    len: usize,
}

impl StackBuf {
    fn new() -> Self { StackBuf { buf: [0; 256], len: 0 } }
    fn as_str(&self) -> &str {
        std::str::from_utf8(&self.buf[..self.len]).unwrap_or("")
    }
}

impl fmt::Write for StackBuf {
    fn write_str(&mut self, s: &str) -> fmt::Result {
        let bytes = s.as_bytes();
        let new_len = self.len + bytes.len();
        if new_len > self.buf.len() { return Err(fmt::Error); }
        self.buf[self.len..new_len].copy_from_slice(bytes);
        self.len = new_len;
        Ok(())
    }
}

// Logger that formats without allocating a String
struct Logger { prefix: &'static str }

impl Logger {
    fn log(&self, args: fmt::Arguments) {
        // Write directly to stderr without String allocation
        let mut buf = StackBuf::new();
        write!(buf, "[{}] ", self.prefix).ok();
        buf.write_fmt(args).ok();
        eprintln!("{}", buf.as_str());
    }
}

macro_rules! log_info {
    ($logger:expr, $($arg:tt)*) => {
        $logger.log(format_args!($($arg)*))
    };
}

// format_args! enables writing to multiple sinks
fn write_to_all(args: fmt::Arguments) {
    // Write to stdout
    println!("stdout: {}", args);
    // Write to a String buffer
    let mut s = String::new();
    write!(s, "{}", args).unwrap();
    println!("  (captured: {:?})", s);
    // Write to stack buffer
    let mut buf = StackBuf::new();
    buf.write_fmt(args).ok();
    println!("  (stack buf: '{}')", buf.as_str());
}

// Deferred formatting
fn log_if<'a>(condition: bool, msg: fmt::Arguments<'a>) {
    if condition {
        println!("Condition true: {}", msg);
    }
    // If false, the format is never evaluated
}

fn main() {
    let logger = Logger { prefix: "INFO" };

    // format_args! โ€” no heap allocation
    log_info!(logger, "Server started on port {}", 8080);
    log_info!(logger, "Request from {} user agent: {}", "127.0.0.1", "curl/7.0");

    // Direct format_args usage
    let name = "World";
    let n = 42;
    write_to_all(format_args!("Hello, {}! Value = {}", name, n));

    // Lazy evaluation โ€” format only happens if condition is true
    log_if(true, format_args!("Count: {}", 100));
    log_if(false, format_args!("Expensive: {}", (0..1000000).sum::<i64>())); // lazy!

    // Stack buffer โ€” truly zero allocation
    let mut buf = StackBuf::new();
    write!(buf, "Pi = {:.6}", std::f64::consts::PI).unwrap();
    println!("Stack formatted: {}", buf.as_str());

    // Demonstrate: format! vs format_args!
    let _with_alloc: String = format!("Hello, {}!", name); // allocates
    // format_args!("Hello, {}!", name)                    // no allocation โ€” lazy
    println!("format! allocates, format_args! doesn't!");
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_stack_buf() {
        let mut buf = StackBuf::new();
        write!(buf, "Hello, {}!", "Rust").unwrap();
        assert_eq!(buf.as_str(), "Hello, Rust!");
    }

    #[test]
    fn test_format_args_zero_alloc() {
        let mut buf = StackBuf::new();
        let args = format_args!("value = {}", 42);
        buf.write_fmt(args).unwrap();
        assert_eq!(buf.as_str(), "value = 42");
    }
}
(* format_args! concept in OCaml *)

(* OCaml's Format module has similar deferred formatting *)

let format_to_buffer buf fmt_fn =
  let b = Buffer.create 64 in
  let f = Format.formatter_of_buffer b in
  fmt_fn f;
  Format.pp_print_flush f ();
  Buffer.add_buffer buf b

(* Format without allocation to a custom formatter *)
let to_stderr fmt_fn =
  let f = Format.formatter_of_out_channel stderr in
  fmt_fn f;
  Format.pp_print_flush f ()

let with_prefix prefix fmt_fn =
  let b = Buffer.create 64 in
  let f = Format.formatter_of_buffer b in
  fmt_fn f;
  Format.pp_print_flush f ();
  prefix ^ Buffer.contents b

let () =
  let buf = Buffer.create 64 in
  format_to_buffer buf (fun f ->
    Format.fprintf f "Hello, %s! Value = %d" "World" 42
  );
  Printf.printf "Buffer: %s\n" (Buffer.contents buf);
  let msg = with_prefix "[INFO] " (fun f ->
    Format.fprintf f "Server started on port %d" 8080
  ) in
  Printf.printf "%s\n" msg