RustArena

Step 2 of 7

Mutable references

A & reference is read-only. Try to change something through one and the compiler stops you:

fn shout(text: &String) {
    text.push_str("!");
}

fn main() {
    let mut s = String::from("hi");
    shout(&s);
    println!("{}", s);
}
error[E0596]: cannot borrow `*text` as mutable, as it is behind a `&` reference
 --> src/main.rs:2:5
  |
2 |     text.push_str("!");
  |     ^^^^ `text` is a `&` reference, so it cannot be borrowed as mutable
  |
help: consider changing this to be a mutable reference
  |
1 | fn shout(text: &mut String) {
  |                 +++

To lend a value for changing, use a mutable reference, &mut. Its type is &mut String, and you create one with &mut s. Three places have to agree:

  1. the owner is declared mut (you can't lend out write access to something you can't write yourself),
  2. the parameter's type is &mut T,
  3. the call passes &mut value.
fn add_signature(letter: &mut String) {
    letter.push_str("\n-- Ferris");
}

fn restock(shelf: &mut Vec<String>) {
    shelf.push(String::from("milk"));
    shelf.push(String::from("eggs"));
}

fn main() {
    let mut letter = String::from("See you in the arena!");
    add_signature(&mut letter);
    println!("{}", letter);

    let mut shelf = vec![String::from("bread")];
    restock(&mut shelf);
    println!("{:?}", shelf);
}
See you in the arena!
-- Ferris
["bread", "milk", "eggs"]

As with &, method calls work through a &mut automatically: letter.push_str(...) changes the String that main owns. When the function returns, the borrow is over and main can use its value again, now changed.

You've used a mutable reference since the input module: std::io::stdin().read_line(&mut line). read_line borrows your String mutably so it can append the typed line to it, then gives the borrow back. Now you know why the &mut is there.

The picture is the same as for &. The difference is a promise the compiler enforces: while a &mut exists, it is the only way to reach the value (step 4 explains why).

   add_signature's frame       main's frame
   letter: &mut String         letter: String
  ┌────────────┐              ┌────────────┐          HEAP
  │ ptr ───────┼────────────► │ ptr ───────┼─────► "See you in the arena!" + more text
  └────────────┘              │ len ...    │
    exclusive access:         │ cap ...    │
    can read and write        └────────────┘

Common mistakes

fn main() {
    let s = String::from("hi");
    let r = &mut s;
    r.push('!');
}
error[E0596]: cannot borrow `s` as mutable, as it is not declared as mutable

Your task

Write add_entry. It appends entry to the log it borrows mutably. If the log already has text in it, put "; " before the new entry, so entries come out as start; hit; miss. (entry: &str lets you pass string literals like "hit". log.is_empty() tells you whether a String has no text.)

Challenge 2

level_up should add a skill to the player's skill list, but the program has three mistakes: the function takes & instead of &mut, the variable isn't mut, and the call passes & instead of &mut. Fix all three (read the compiler's messages one at a time) so it prints:

["run", "jump", "double jump"]

Challenge 3

Write pad_right. It borrows text mutably and pushes . characters onto the end until the text is width characters long. If it's already that long or longer, leave it unchanged. Count characters with .chars().count(), so "café" padded to 6 becomes "café..".

Previous: References: borrowing a value Next: Changing values through &mut with *