TIL: format!() does not necessarily pre-allocate the optimal size for the resulting string

144 points anxxa 31 comments

I have no idea if I'm late to the party on this, but while doing some perf work I was surprised to see that format!() was a big contributor to reallocations in the target.

After some digging I was surprised to learn that the output string capacity receives an estimate that is neither considered an upper nor lower bound: https://github.com/rust-lang/rust/blob/1ed2df61a19042f231709eb05d032ae9e2cb2084/library/core/src/fmt/mod.rs#L749-L808

The change which first introduced the estimate output capacity was in 2017, which notes that it explicitly uses the literal portions of the string for the estimate: https://github.com/rust-lang/rust/pull/39356

This is clearly visible here: https://rust.godbolt.org/z/rfcTbMa6K

tl;dr of the assembly: rdx is one of the string's lengths, and is only accessed for len + 1 calculation in the source which only exists to help track the length.

The emitted code in my case is essentially:

// 0 because there is a 1-char string literal in the template and the template
// starts with a placeholder.
// See: https://github.com/rust-lang/rust/blob/1ed2df61a19042f231709eb05d032ae9e2cb2084/library/core/src/fmt/mod.rs#L795-L800
let estimate = 0;
let mut output = String::with_capacity(estimate);
output.push_str(arg1); // causes an alloc
output.push('/'); // might realloc
output.push_str(arg2); // might also realloc

I've apparently been under a false assumption that for some types the runtime length could be used to help size the output string appropriately.