There's also `std.fmt.allocPrint()` which functions similarly to `format!()`. Although I'd argue its rather poorly named, like many of the functions in the stdlib...
For wanting to avoid fiddling with multiple repeated alloc/defer-free, it's often convenient to use the arena allocators that allow you to release everything with a single `defer arena.deinit()`
I would (coming from a C background) guess that `allocPrint()` owes its name from the C standard library function(s) `as(n)printf()`[1]. At least that would make sense, the naming is modernized by being made longer, to gain clarity.
(Make the string "10. Fortress" from the int 10 and "Fortress")
The reason is, most of the strings in my game are bounded, and actually, known ahead of time. None of the levels have names that approach anything as long as 64 bytes, so I can actually do a fair bit of string manipulation on the stack before needing to use / save it to an allocator. (At least for now before localization XD)
So it depends on the use case. Sure, string manipulation in general can be tiresome in Zig, but in many cases it's also simple.
In safe build modes (ReleaseSafe or Debug) it's an immediate panic, equivalent to calling `.unwrap()` on a None in Rust. In unsafe build modes it's undefined behavior.