#262 Jul 16, 2026

262. String::insert_str — Prepend or Splice Text Without Rebuilding the String

Adding a prefix with format!("{prefix}{s}") builds a brand-new String and throws the old one away. insert_str splices the text into the buffer you already have.

The trap

You have a String and need to stick something in front of it — a log level, a scheme, a marker. The reflex is format!:

1
2
3
4
5
6
let mut msg = String::from("connection lost");

// allocates a second String, copies both halves,
// drops the original
msg = format!("[ERROR] {msg}");
assert_eq!(msg, "[ERROR] connection lost");

That’s a full new allocation and two copies just to add eight bytes at the front. push_str only helps at the end — there’s no push_front for strings.

The fix

String::insert_str shifts the existing bytes over and copies the new text in, reusing the allocation when capacity allows:

1
2
3
4
let mut msg = String::from("connection lost");

msg.insert_str(0, "[ERROR] ");
assert_eq!(msg, "[ERROR] connection lost");

And it’s not just for prepending — the index can be anywhere, which makes splicing into the middle a one-liner:

1
2
3
4
5
6
let mut name = String::from("report_final.txt");

if let Some(dot) = name.rfind('.') {
    name.insert_str(dot, "_v2");
}
assert_eq!(name, "report_final_v2.txt");

For a single character there’s the sibling String::insert(idx, char).

One caveat

The index is a byte offset and must land on a char boundary — mid-emoji it panics, same rule as slicing. And since the tail gets shifted each call, insert_str is O(n): perfect for the occasional splice, wrong for building a string front-to-back in a loop. If you’re prepending repeatedly, collect the pieces and join them once instead.

← Previous 261. String::retain — Delete Characters In Place, No New Allocation