Strings and Text Processing
String vs. &str
String and &str are complementary UTF-8 types that parallel the relationship between Vec<T> and &[T]: String owns and mutates a growable heap buffer, while &str borrows an immutable view into UTF-8 text.
Side-by-Side Comparison
| Dimension | String |
&str |
|---|---|---|
| Ownership | Owned (Drop frees the heap buffer) |
Borrowed view (no cleanup on drop) |
| Mutability & growth | Growable (.push(), .push_str(), .clear()) |
Fixed length; immutable bytes |
| Stack size (64-bit) | 24 bytes (ptr, len, capacity) |
16 bytes (ptr, len) |
| Backing storage | Always heap-allocated (when capacity > 0) |
Binary .rodata, heap, or stack |
Implements Copy? |
No (moves or clones heap buffer) | Yes (copies the 16-byte fat pointer in $O(1)$) |
| Parallel collection type | Vec<u8> |
&[u8] |
s: String (24 B on stack)
+---------------+---------------+---------------+
| ptr | len = 11 | capacity = 16 |
+-------+-------+---------------+---------------+
|
v
Heap: [ 'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', _, _, _, _, _ ]
▲ ▲
| |
slice1: &str (ptr, len = 5) slice2: &str (ptr + 6, len = 5)
Why Function Parameters Should Prefer &str Over &String
Accepting &str as a function parameter allows callers to pass both string literals (&'static str), substrings (&s[a..b]), and borrowed String values (&my_string, via deref coercion) without allocating:
// Accepts `&str`, `&String` (via Deref), and string slices `&s[..]`:
fn has_prefix(text: &str, prefix: &str) -> bool {
text.starts_with(prefix)
}
fn main() {
let owned = String::from("production-db-01");
let literal = "staging-db-01";
println!("From &String: {}", has_prefix(&owned, "prod"));
println!("From &str: {}", has_prefix(literal, "prod"));
println!("From slice: {}", has_prefix(&owned[11..], "db"));
}
From &String: true
From &str: false
From slice: true
Choosing Between String, &str, and Cow<'a, str>
| Scenario | Recommended Type |
|---|---|
| Read-only function parameter | &str (or impl AsRef<str>) |
| Function builds and returns new text | String |
| Struct field that must own its data without lifetime parameters | String (or Box<str> if never mutated) |
Zero-copy parser struct tied to an input buffer's lifetime 'a |
&'a str |
| Function usually returns input unchanged, but occasionally allocates | std::borrow::Cow<'a, str> |