Rust Borrowing and References
On the ownership page, every time we passed a String to a function, we lost it. That would get annoying very quickly. In real life, if a friend wants to read your book, you do not give it to them forever; you lend it, and you get it back. Rust has exactly this idea, and it is called borrowing.
References with &
A reference lets a function use a value without taking ownership of it. You make one by putting & in front of the value, and the function says it expects a reference by putting & in front of the type:
fn count_letters(text: &String) -> usize {
text.chars().filter(|c| c.is_alphabetic()).count()
}
fn main() {
let name = String::from("Rustacean");
let letters = count_letters(&name);
println!("{} has {} letters", name, letters);
}Rustacean has 9 letters
After the call, name still belongs to main and can be printed. The function only borrowed it. When the function ends, the borrow ends, and nothing is dropped, because the function never owned anything.
Changing a borrowed value with &mut
A normal reference is read-only, like a library book you must not write in. If a function needs to change the value, it asks for a mutable reference, written &mut. The variable itself must be mut too:
fn add_greeting(text: &mut String) {
text.push_str(", have a great day!");
}
fn main() {
let mut message = String::from("Hello Sam");
add_greeting(&mut message);
println!("{}", message);
}Hello Sam, have a great day!
This is what read_line(&mut name) has been doing all along: it borrows your String mutably and writes the typed line into it.
The golden rule: many readers or one writer
At any moment, you can have either as many read-only references as you like, or exactly one mutable reference, but never both at the same time. Picture a shared document: many people can read it together, but while one person is editing, nobody else should be reading a half-changed version.
fn main() {
let mut scores = vec![10, 20, 30];
let first = &scores[0];
let last = &scores[2];
println!("first {} last {}", first, last);
let editor = &mut scores;
editor.push(40);
println!("now {:?}", scores);
}first 10 last 30 now [10, 20, 30, 40]
This works because the two readers, first and last, are finished before the writer starts. Rust is clever about this: a borrow lasts only until its last use. If you moved the println! of first below editor.push(40), the compiler would stop you, because a reader would still be looking while the writer changes the list. The error is number E0502, and the hint in the editor explains it in plain words.
Why this rule is such a big deal
The many-readers-or-one-writer rule makes a whole class of bugs impossible: data changing underneath code that is reading it. In other languages such bugs appear randomly and are very hard to find. In Rust they are caught while compiling, every time. That is the real reward for learning borrowing.
Practice exercises
- Write
fn print_twice(text: &String)and call it twice with the sameString. - Write
fn double_all(numbers: &mut Vec<i32>)that multiplies every number by 2. - Move
println!("first {} last {}", first, last);beloweditor.push(40);and read the error.