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.