Box<str> vs String in Rust: How to Explain It in an Interview
ยท rust, coding interview, technical interview
Rust interviews often move past basic syntax and into ownership and memory questions. One that comes up is the difference between Box<str> and String. Both look like owned strings, so many candidates get stuck on this one.
This article explains how each type is built and when to choose one over the other. It also gives you a short spoken answer, a few follow-up questions to prepare for, and common mistakes to avoid.
What the Two Types Have in Common
Start with the similarities. Interviewers like it when you show you understand both sides before comparing.
- Both are owned types. When the value is dropped, its heap memory is freed.
- Both store their text on the heap.
- Both always hold valid UTF-8 text.
- Both deref to str, so you can call the usual str methods on them, such as len, contains, starts_with and split.
- Both can be borrowed as a &str cheaply, without copying the text.
So the difference is not about safety or encoding. It is about growth and size.
The Key Difference: Growable vs Fixed
A String is a growable buffer. Internally it wraps a Vec<u8>. It stores three pieces of information: a pointer to the heap data, the length, and the capacity. Capacity is how much space is reserved, which can be more than the length. That spare room lets you call push_str or push without reallocating every time.
A Box<str> is an owned, fixed-size string slice. It is a fat pointer with only two pieces of information: a pointer to the data and the length. There is no capacity field, because the type cannot grow. You cannot call push_str on it.
On a typical 64-bit platform, this means:
- String takes 24 bytes on the stack (pointer, length, capacity).
- Box<str> takes 16 bytes on the stack (pointer, length).
A Box<str> also never holds unused heap capacity. Its heap allocation is exactly as big as the text.
One small detail is worth knowing. A Box<str> is not completely immutable. You can get a &mut str from it and call in-place methods like make_ascii_uppercase. What you cannot do is change its length.
Converting Between Them
Interviewers sometimes ask how you move from one to the other. Know these conversions:
- String to Box<str>: call into_boxed_str on the String. If the String has extra capacity, it is shrunk to fit first, which may cause a reallocation.
- Box<str> to String: call into_string on the box, or use String::from. This reuses the same allocation, so no copy is needed.
- &str to Box<str>: use Box::from on the string slice, or call into with the target type. This allocates and copies the text.
A good point to mention is the cost. Going from Box<str> to String is cheap. Going the other way can cost one reallocation if there was spare capacity.
When to Use Each One
Use String when the text will change. This covers building output in a loop, reading input line by line, or formatting messages. String is also the default choice in most code, and that is fine.
Consider Box<str> when the text is set once and then only read, especially if you store many of them. Some examples:
- A struct holding a name or ID that never changes after creation.
- A large vector or hash map of fixed strings, such as keys loaded from a file.
- A long-lived cache where saving 8 bytes per entry, plus any unused capacity, adds up.
The type also tells other readers something. A field of type Box<str> clearly says that this text is not meant to grow.
Related types are worth mentioning if the conversation goes further:
- &str is a borrowed view, with no ownership.
- Rc<str> and Arc<str> are fixed strings with shared ownership. Cloning them only bumps a reference count instead of copying the text. Arc<str> works across threads.
Be honest about trade-offs too. For most everyday code, the memory saving is small. String is easier to work with, and switching to Box<str> without a reason can add noise.
A Sample Answer You Can Adapt
Here is a short answer a candidate might give in a technical round:
"Both String and Box<str> own UTF-8 text on the heap, and both deref to str. The difference is that String is growable. It stores a pointer, a length and a capacity, so it is 24 bytes on a 64-bit target and can hold extra unused space. Box<str> is a fixed-size owned slice. It stores only a pointer and a length, so it is 16 bytes and has no spare capacity. I would use String when I need to build or modify text. I would use Box<str> for strings that are set once and stored in large numbers, like keys in a big map. I can convert with into_boxed_str, which may shrink the allocation, and back with into_string, which does not copy."
This answer works because it is structured. It covers similarities, then the difference, then use cases, then conversions.
Follow-Up Questions to Prepare For
Practise answering these out loud:
- Why is Box<str> called a fat pointer? Because str is a dynamically sized type, so the pointer must carry the length along with the address.
- Can you push to a Box<str>? No. Convert it to a String first, modify it, then convert back if needed.
- Does into_boxed_str always allocate? No. It only reallocates if the String has more capacity than length.
- How would you share one fixed string across threads? Use Arc<str>.
- Would you change all String fields to Box<str>? Usually not. Only where the text is fixed and the memory saving matters, ideally after measuring.
Common mistakes to avoid:
- Saying Box<str> is stored on the stack. The pointer is on the stack, but the text is on the heap.
- Confusing Box<str> with &str. One owns its data and the other borrows it.
- Claiming Box<str> is always faster. It saves memory, but speed depends on how it is used.
If you can explain the layout, the conversions and one real use case calmly, you will handle this question well. It also helps to practise saying your answer aloud with a mock interview tool such as Recallr.