String, str and Unicode text
String, str and Unicode text
Student names do not always fit one byte per letter. Rust stores String and str text as UTF-8. String owns a growable buffer; str is a text slice type, commonly accessed through &str. Understanding that distinction prevents incorrect indexing and unnecessary ownership transfers.
What you’ll learn
- Choose String ownership or an &str text view.
- Distinguish UTF-8 bytes, Unicode scalars and grapheme clusters.
String, str and Unicode text in action
Zoë: UTF-8 text
Z and o each use one byte, while ë uses two in UTF-8.
Zoë: UTF-8 text4 bytes versus 3 scalars
The text therefore has four bytes and three scalar values.
4 bytes versus 3 scalarsAppend class label
Appending the class label grows the owned String.
Append class label
String, str and Unicode text explained
Use push_str to append borrowed text to a mutable String. len() counts bytes and chars() yields Unicode scalar values. Rust does not provide direct integer indexing for String because a byte position does not consistently identify a character. User-perceived grapheme clusters can contain multiple scalar values.
Connect this idea with references and read-only borrowing and slices and reference lifetimes.
Work through the Rust program
Save the complete source as main.rs in the Rust environment. No standard input is needed.
Rust 2024 · complete program
fn main() {
let mut name = String::from("Zoë");
println!("Bytes: {}", name.len());
println!("Scalars: {}", name.chars().count());
name.push_str(" - A");
println!("{name}");
}Expected output
Bytes: 4
Scalars: 3
Zoë - AFollow the execution
Z and o each use one byte, while ë uses two in UTF-8. The text therefore has four bytes and three scalar values. Appending the class label grows the owned String. The original accent remains valid text.
Try the complete program in the Rust compiler. For tests, Cargo commands and local files, use the specified local environment.
Try a small variation
Your task: Is name.len() a reliable count of letters shown to a reader?
Rust 2024 · complete program
fn main() {
let mut name = String::from("Zoë");
println!("Bytes: {}", name.len());
println!("Scalars: {}", name.chars().count());
name.push_str(" - A");
println!("{name}");
}Worked answer
No. It counts UTF-8 bytes. Even chars().count() counts scalar values rather than every user-perceived grapheme cluster.
Bytes, scalar values and displayed letters
Slicing a string with byte positions can panic if a boundary splits a UTF-8 encoding. Grapheme processing needs a suitable library. Prefer &str for a function that only reads text and String when the function needs owned, growable text.
API details: official Rust documentation for this boundary.
Check your understanding
Take the idea into your next program
No. It counts UTF-8 bytes. Even chars().count() counts scalar values rather than every user-perceived grapheme cluster. Slicing a string with byte positions can panic if a boundary splits a UTF-8 encoding.
Further reading: the official Rust Book on string, str and unicode text.
Reaching the recap and completing the quizzes are separate milestones.