Slices and strings
A String owns its data while a &str borrows it; slices are views into a sequence and must respect UTF-8 boundaries.
- Use String when you own and grow text, and &str when you only need to read it.
- Take string slices with &text[a..b] and array slices with &values[a..b].
- Remember a slice borrows data, so the original must live as long as the slice is used.
- Never split a string in the middle of a multi-byte UTF-8 character.
In plain English
In Rust every value has a single owner, and when the owner goes out of scope the value is dropped, which means its memory is freed; ownership is how Rust avoids garbage collection and double-frees. A String owns its text on the heap and can grow, while a &str is a borrowed reference to some string data and cannot change it, which is why you can pass a &str around cheaply without giving up ownership. A slice is a borrowed view into a contiguous sequence, written &text[a..b] for strings or &values[a..b] for arrays, and it never owns the data it points at. Rust strings are stored as UTF-8, so a character can take several bytes, and slicing in the middle of a character makes the program panic instead of silently corrupting the text.
Step by step
- 1Create an owned String, then borrow a &str from it.
- 2Take a substring with &text[a..b] using byte indices.
- 3Borrow part of an array with &values[a..b].
- 4Check a string length in bytes with len() and in characters with chars().count().
fn main() {
let owned = String::from("hello");
let view: &str = &owned;
println!("{} {}", owned, view);
let greeting = String::from("cafe");
let part = &greeting[0..3];
println!("{}", part);
let scores = [10, 20, 30, 40, 50];
let middle = &scores[1..4];
println!("{}", middle[0]);
let name = "résumé";
println!("bytes {}", name.len());
println!("chars {}", name.chars().count());
}Common mistakes
- Slicing a string at an index that lands in the middle of a multi-byte UTF-8 character panics, because Rust will not produce invalid text.
- Treating a &str as if it owns its data is wrong; it is a borrow, so the data it points at must outlive it.
- Comparing a String and a &str directly without borrowing the String with & will not compile in many cases.
- Assuming len() counts characters is wrong, because it counts bytes, and multi-byte characters make those numbers differ.
Borrow the first three bytes
Write a function first_three(text: &str) -> &str that returns the first three bytes of a string.
fn first_three(text: &str) -> &str {
text
}What does name.len() return for a UTF-8 string?
Make it yours.
Keep a private note for this lesson, then turn the key ideas into flashcards you can revisit.
Lock in what you just learned.
Mark this lesson complete to save your place and move one step closer to the certificate.