RSTR-PERF-001 — Rust format! accumulator in a loop

Summary

A format!(...) macro is invoked inside a loop and the result is appended onto a String (via push_str or +=). Each iteration allocates a fresh temporary String, copies it onto the accumulator, then drops the temporary. Total allocations are O(n), total bytes copied are O(n²).

Severity

Medium. The wall-clock impact only shows up at loop counts in the thousands, but the fix is a one-line refactor and turns the algorithm from quadratic to linear.

Languages

Rust.

What rastray flags

#![allow(unused)]
fn main() {
let mut s = String::new();
for x in xs {
    s.push_str(&format!("{x},"));        // ← flagged
}
}
#![allow(unused)]
fn main() {
for line in lines {
    output += &format!("- {line}\n");     // ← flagged
}
}

What rastray deliberately does not flag

  • format! outside loops.
  • format! whose result is returned, stored, or passed elsewhere (not accumulated).
  • write!(&mut s, "...") — the safe replacement.

How to fix it

Write directly into the accumulator with write! from std::fmt::Write:

#![allow(unused)]
fn main() {
use std::fmt::Write;

let mut s = String::new();
for x in xs {
    write!(&mut s, "{x},").unwrap();   // unwrap is OK; String's Write impl never fails
}
}

For known capacity, pre-allocate:

#![allow(unused)]
fn main() {
let mut s = String::with_capacity(xs.len() * 4);
}

Combined, the loop runs in linear time with at most one realloc.

References