In this lesson, you will learn the fundamental difference between the two primary ways Rust handles text: the fixed-size string slice and the growable string buffer.
The String Slice: &str
A string slice, written as &str, is a "view" into a collection of UTF-8 bytes. Because it is just a reference, it does not own the data it points to. Think of it as a pointer to a specific memory address and a length counter. Because it is a view, it is typically stack-allocated (or points to static memory in your binary) and cannot change in size.
let greeting: &str = "Hello!"; // Lives in the binary's data section
Since &str is immutable and fixed in size, it is incredibly fast and efficient. However, because it cannot grow or shrink, it isn't useful if you need to build or modify a string at runtime.
The Dynamic Buffer: String
When you need to manipulate text—like appending characters or reading user input—you need a String. A String is a wrapper around a Vec<u8> that lives on the heap.
Because it lives on the heap, a String owns its data. It has the flexibility to grow by reallocating more memory if you add enough characters to exceed its current capacity.
let mut name = String::from("Rust"); // Heap-allocated
name.push_str("acean"); // Safe to grow!
The UTF-8 Constraint
Both &str and String enforce one golden rule: the data must be valid UTF-8. Rust doesn't allow you to store arbitrary bytes in these types; it ensures that your strings are always correctly encoded. If you try to create a String from invalid bytes, the program will panic or refuse to compile, protecting you from common encoding bugs found in other languages.
In short: use &str when you want a lightweight, read-only view of existing text, and use String when you need an owned, growable container for text that lives on the heap.
Key Takeaway: &str is a fixed, stack-allocated view of UTF-8 data, while String is a growable, heap-allocated buffer that owns its contents.