What String does
A growable, owned, UTF-8 encoded text buffer.
As a struct, String bundles related fields into a single named record with a known layout. Each field has a fixed type and offset, so the size of an instance is the sum of its field sizes plus alignment padding. Construction follows ordinary Rust ownership rules: each owned field must itself be constructed or moved before the struct is well-formed, and the compiler will refuse to leave any field uninitialised. The full canonical path is std::string::String; bring it into scope with use std::string::String; or refer to it by its full path.
When to use it
Use String for owned, mutable text. Use `&str` for borrowed text — function parameters should almost always be `&str`, and you only need a `String` when you must own or grow the buffer.
Annotated examples
Building a String with format! and push_str
fn greeting(name: &str, count: u32) -> String {
// format! is the simplest way to build a String from a template.
// Internally it creates a Formatter and calls write! on it.
let mut s = format!("Hello, {name}!");
if count > 1 {
// push_str appends a &str to an existing String without
// re-allocating the format machinery.
s.push_str(&format!(" ({count} visits)"));
}
s
}
format! is convenient for one-shot construction; for larger buffers, allocate with String::with_capacity and use push_str / write! to avoid repeated allocations.
For deeper background, see the canonical Rust patterns reference for the broader context behind this section.
Iterating over chars (not bytes) safely
fn first_word(s: &str) -> &str {
// chars() yields full Unicode scalar values, not bytes.
// For ASCII-only data, bytes() is faster but indexing into
// a String by byte offset can land in the middle of a codepoint.
match s.find(char::is_whitespace) {
Some(idx) => &s[..idx],
None => s,
}
}
String is UTF-8 — never slice a String by raw byte index unless you know the boundary is on a codepoint. find returns valid byte offsets you can slice with safely.
Common pitfalls
String slicing by byte index will panic at runtime if the index lands inside a multi-byte UTF-8 sequence — `&s[0..1]` is unsafe to assume on arbitrary strings. Use `chars().take(n).collect::<String>()` if you need the first n characters by codepoint, or `s.char_indices()` to recover safe byte boundaries for slicing.
For deeper background, see an in-depth Rust idioms cheat sheet for the broader context behind this section.
Performance & threading notes
Stack-allocated by default. Heap allocation only happens if a field is itself heap-allocated (Vec, String, Box). Cloning copies every field; for read access pass by reference. Send / Sync are automatically derived if every field is Send / Sync.