Rust Foundations·Ownership as a design tool

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.

Intermediate 14 min read Lesson 3 of 300 Challenge included
What you will be able to do
  • 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

  1. 1Create an owned String, then borrow a &str from it.
  2. 2Take a substring with &text[a..b] using byte indices.
  3. 3Borrow part of an array with &values[a..b].
  4. 4Check a string length in bytes with len() and in characters with chars().count().
main.rsRust
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.
Your turn

Borrow the first three bytes

Medium

Write a function first_three(text: &str) -> &str that returns the first three bytes of a string.

RUST · starter
fn first_three(text: &str) -> &str {
    text
}
Check your understanding before moving on.
Knowledge checkQuestion 1

What does name.len() return for a UTF-8 string?

Select an answer to continue
Study tools

Make it yours.

Keep a private note for this lesson, then turn the key ideas into flashcards you can revisit.

Sign in to save notes and flashcards.Both are private to your account and stay with your progress.
Sign in
Finished reading?

Lock in what you just learned.

Mark this lesson complete to save your place and move one step closer to the certificate.

Next: Traits and generics
Lesson 3 of 300 · 0% of course