Skip to content
Lesson 9 of 15

Step 1 of 8 · Reading · ~3 min

Learn

Functions and Ownership

Ownership and Borrowing

By the end of this lesson you can pass a String to a function without losing it, let a function read it through &, and let a function change it through &mut.

Ownership: one owner, and moves

Every value in Rust has exactly one owner, the variable responsible for it. When the owner goes out of scope, the value is dropped (its memory is freed). Assigning a String to another variable moves it: the new variable becomes the owner and the old one can no longer be used.

let city = String::from("Oslo");
let moved = city;              // ownership moves to `moved`
// println!("{}", city);       // error[E0382]: borrow of moved value: `city`
println!("{}", moved);         // Oslo

Passing a String to a function whose parameter is String moves it the same way: the function owns it now, and the caller cannot touch it afterwards. Small stack values such as i32, f64, bool and char are Copy: assignment duplicates them and both variables stay usable. For a String, .clone() makes an independent copy, but it allocates, so use it only when you really need two copies.

Borrowing to read: &

A borrow (a reference) lets a function use a value without owning it. &title creates a shared, read-only reference; the caller keeps ownership.

fn space_count(s: &str) -> usize {
    let mut n = 0;
    for c in s.chars() {
        if c == ' ' { n += 1; }
    }
    n
}

let title = String::from("a tale of two");
println!("{}", space_count(&title));   // 3
println!("{}", title);                 // still usable: a tale of two

Take &str rather than &String for read-only text: a &String converts to &str automatically, and string literals already are &str.

Borrowing to change: &mut

A mutable reference &mut String lets a function change the caller's value in place. Both sides must say so: the variable is declared let mut, and the call passes &mut note. Methods such as push and push_str work straight through the reference:

fn add_period(s: &mut String) {
    s.push('.');
}

let mut note = String::from("done");
add_period(&mut note);
println!("{}", note);                  // done.

The trap: a new String that goes nowhere

Many string methods (trim, to_uppercase, to_lowercase, repeat) do not change the string. They return a new value. Calling one through a &mut reference and ignoring the result changes nothing:

fn tidy(s: &mut String) {
    s.trim();                          // WRONG: result thrown away
}
// "  hi  " stays "  hi  "

To replace the caller's whole value, assign through the reference with *. The * means "the value this reference points at":

fn tidy(s: &mut String) {
    *s = s.trim().to_string();         // right side is built first, then stored
}
// "  hi  " becomes "hi"

Without the *, s = ... tries to assign a String to the reference itself and does not compile.

One writer or many readers

At any moment a value can have many & borrows or exactly one &mut borrow, never both. The compiler tracks where each reference is last used, so taking a &mut borrow is fine once the readers are finished with theirs. This rule is how Rust rules out data races at compile time.

Common mistakes

  • Calling to_uppercase() through the &mut and discarding the result: the line prints unchanged, hello world! instead of HELLO WORLD!.
  • Counting only lowercase vowels: once the text is uppercased the second count drops to 0, and input such as AEIOU is undercounted even before it is.
  • Appending ! unconditionally: stop it! becomes STOP IT!! instead of STOP IT!.
  • Writing s = ... instead of *s = ...: a compile error, because you are assigning to the reference, not to the String.

Your exercise

Fill in vowel_count, which only reads its &str, and make_loud, which changes the caller's String through &mut. main is already written and uses line after both borrows. ends_with is the mirror image of starts_with. The most likely mistake is building the uppercase text and never storing it back through the reference.

Up nextVec: Growable ArraysCollections

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…