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:
- the owner is declared
mut(you can't lend out write access to something you can't write yourself), - the parameter's type is
&mut T, - 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
- Forgetting
muton the variable. You can't borrow mutably from an immutable variable:
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
- Writing
&at the call when the function wants&mut:error[E0308]: mismatched typeswith the notetypes differ in mutability. - Asking for
&mutwhen you only read. Callers then need amutvariable, and (step 4) nobody else can look at the value while your borrow is active. Ask for the least access you need.
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 *