Rust’s format!
macro is slow for simple string concatenation. But why?
29 March, 2025
Why is the format!
compiler built-in macro so much slower for string concatenation than doing it “manually” by calling String::with_capacity
followed by a series of String::push_str
?
I’m going to test this with a benchmark that will join 3 variables together with a space 100,000 times using both the format!
macro and the manual method.
// We'll need black_box to make sure the
// compiler does not make any optimizations that
// would ruin the benchmark.
// For instance it could concatenate the strings at compile time
// as an optimization. We want to simulate how it would be like in
// real code, where we don't know what the values will be.
use black_box;
use Instant;
These are the results, running in --release
mode:
concat_manual: 1.879225ms
concat_format: 9.984558ms
Using format!
is about 5x slower than preallocating the correct amount of memory, then pushing the strings manually.
My question is why. Since format!
is built-in, at compile time the Rust compiler should be able optimize a simple use of format!
that is only for string concatenation to be just as fast as using the “manual” approach of concatenating the string.
I am aware that strings passing through the std::fmt
machinery have to do more work. But couldn’t this extra work be skipped in more simple cases such as string concatenation?
In theory, the Rust compiler could optimize the format!
macro and friends to use a different, much simpler algorithm if none of the fancy formatting options are used, so only the Display
impls are.
Using format!
is so much nicer than having to resort to manual string preallocation then pushing into a buffer, and used quite a lot in Rust. I would love to see this area get some performance improvements.
Declarative utility macro for efficient string concatenation
I’ve written a declarative macro that can concatenate strings in a more efficient way. Granted, this efficiency is unlikely to matter in most situations since format_args!
is still incredibly fast.
/// Concatenates strings together.
///
/// `str_concat!(a, " ", b, " ", c)` is:
/// - more performant than `format!("{a} {b} {c}")`
/// - more ergonomic than using `String::with_capacity` followed by a series of `String::push_str`
However, there’s hope!
As it turns out, the library team is absolutely aware about this. In fact there are efforts to make this formatting faster.
Thanks to u/joboet
for these 2 additional points:
- Since all
format_args!
invocations must create afmt::Arguments
structure, and (due to std being precompiled) that structure cannot be changed dynamically, the solution needs to be one-size-fits-all: it needs to be fast to run and compile in both debug and release builds, ideally irrespective of how complex the format string is.- Due to the way bootstrapping the Rust compiler works, it’s currently very difficult to make changes to
format_args!
, as you need to maintain the old definition offmt::Arguments
to support bootstrapping the initial compiler.