🤔It all started with the question that: what difference between String and string literals (&str)?
This post is more about my thought process and what I discovered along the way.
đź§µ String Literals (&str) vs String
String literals (&str) represent fixed, immutable sequences of UTF-8 bytes stored directly in the program's binary. They are known at compile time, making them efficient with no heap allocation.
String is a growable, owned, mutable type allocated on the heap. It allows dynamic modifications like appending text via methods such as push_str().
| Aspect | String Literal (&str) |
String |
|---|---|---|
| Mutability | Immutable | Mutable |
| Memory | Stack/binary (fixed size) | Heap (growable) |
| Creation |
"hello" or &str
|
String::from("hello") |
| Performance | Faster access, no allocation/ Faster allocation | Slower due to heap ops |
| Use Case | Static text, function params | Dynamic string building |
String literals coerce to &str for borrowing, while String owns its data and can be converted to &str via as_str().
💠So then I thought: “Ok, string literals are stored in stack and String is stored in heap?”
Wrong — that’s a common misconception.
String is indeed heap-allocated, but string literals (like "hello") reside in the program's read-only binary data section, often called static memory.
đź§ Memory Layout
| Type | Storage Location | Details |
|---|---|---|
String |
Heap | Growable buffer; pointer, len, capacity stored on stack |
&str |
Read-only binary section | Pointer + length on stack; content embedded in binary |
The &str fat pointer (pointer + length) lives on the stack or in registers temporarily, but the literal bytes are baked into the executable — not stack or heap. This makes literals zero-cost and immutable.
⚡ Then I wondered: “Faster allocation for string literals — what does it really mean?”
🔹 String Literal (&'static str)
let s = "hello";
-
"hello"is stored inside the compiled binary - Placed in the program’s read-only memory section
- At runtime, Rust just copies: pointer + length
- No heap allocation or system calls — extremely fast
🔹 String
let s = String::from("hello");
- Allocates memory on the heap
- Copies
"hello"into that heap memory - Stores pointer, length, and capacity on the stack
- Slower than just referencing static memory
🔹 Comparison Table
| Type | Runtime Allocation? | Speed |
|---|---|---|
&'static str |
❌ None | Very fast |
String |
âś… Heap allocation | Slower |
String literal isn’t “faster at allocating” — it simply avoids runtime heap allocation.
💡 That’s when it clicked: “String literals are precompiled fixed values inside the program itself, and not created when the program runs”
🔹 What Happens at Compile Time
let s = "hello";
-
"hello"is placed into the program’s read-only data section - Becomes part of the final binary
- Memory is fixed and known ahead of time
So the compiled executable already contains:
... machine code ...
... read-only data: "hello" ...
🔹 What Happens at Runtime
- Rust does NOT allocate memory or create a new string
- Only creates a small reference on the stack: pointer + length
String literals are:
- âś… Precompiled
- âś… Fixed size
- âś… Immutable
- âś… Stored in read-only memory
- âś… Live for the entire program (
'static)
They cannot grow, shrink, or be freed.
🔹 Compare with String
let s = String::from("hello");
- Allocates memory on the heap
- Copies
"hello"into it - Managed by Rust, freed when out of scope
Dynamic vs static, mutable vs immutable — that’s the big difference.
🔜 What’s Next? Part 2
Thinking ahead, I realized there’s a lot more to Rust strings than just memory and mutability. In Part 2, I want to explore how Rust’s String and string literals compare to strings in languages like JavaScript, which feel simpler but less strict. I’m curious about which is safer to use, when to prefer String over &str, and how their lifetimes work under the hood — basically, how Rust enforces safety and ownership in ways most high-level languages don’t. Can’t wait to dig into these details and continue this learning journey.
Top comments (0)