What borrowing is
Assigning a String to another variable moves it, and the old variable can't be used any more. Most of the time that is not what you want. You only want to look at a value, or change it briefly, and let the original owner keep it. A reference does that, and creating one is called borrowing. A borrow never takes ownership: the owner stays the owner, and the reference is a temporary way to reach the same value.
Rust has two kinds of reference:
&valueis a shared (immutable) reference. It gives read-only access.&mut valueis a mutable reference. It gives read and write access.
Reading through &
let city = String::from("Oslo");
let peek = &city;
println!("{}", peek); // Oslo
println!("{}", city); // Oslo: nothing moved
Both the reference and the owner can be read, because reading changes nothing. You can also have as many shared references to one value as you like at the same time.
Writing through &mut
let mut city = String::from("Oslo");
let editor = &mut city;
editor.push_str(", Norway");
println!("{}", city); // Oslo, Norway
The owner must be declared with mut before you can take a &mut to it, because you can't hand out write access to something that is not writable. Method calls such as push_str work directly on the reference, so no * is needed. For plain values like integers you do write it: with let r = &mut n; the update is *r += 1;, and a variable n that started at 5 is then 6.
The borrowing rules
At any moment, a value can have either any number of & references or exactly one &mut reference. Never both, and never two &mut. The compiler enforces this, and breaking it is an error. Two mutable borrows fail like this:
error[E0499]: cannot borrow `greeting` as mutable more than once at a time
first mutable borrow occurs here
second mutable borrow occurs here
first borrow later used here
Mixing a shared and a mutable borrow while both are still in use fails in the same way:
error[E0502]: cannot borrow `greeting` as mutable because it is also borrowed as immutable
immutable borrow occurs here
mutable borrow occurs here
immutable borrow later used here
The rule exists for a good reason. If someone could change a value while someone else is reading it, the reader could see half-finished data. Rust prevents that at compile time instead of at run time.
A borrow ends at its last use
The phrase "while both are still in use" matters. A borrow does not last until the end of the block. It lasts until the reference's last use. If you finish with a shared reference before you create a mutable one, both can live in the same function, and the program compiles.
In the error above, the last line used the old shared reference after the mutable one was created, so their lifetimes overlapped. Move that use earlier and the error disappears.
The exercise, step by step
The task is to read a string through a shared reference, then append to it through a mutable reference, and print both stages. The expected output is:
reader sees = Hello
greeting = Hello, Rust!
- Create the owner.
greetingis aString::from("Hello"), declared withmutbecause a mutable borrow will be taken later.String::fromgives an owned, growable string. A literal such as"Hello"is a borrowed&strand can't grow. - Borrow it for reading.
readeris&greeting, with nomut. - Print through the reader. This is the reader's last use, so its borrow ends on this line.
- Borrow it for writing.
writeris&mut greeting. It is allowed because no shared borrow is still alive. - Append.
push_str(", Rust!")on the writer changes the original string in place. - Print the owner.
greetingis usable again because the writer is not used after this point, so its mutable borrow is over.
let mut greeting = String::from("Hello");
let reader = &greeting;
println!("reader sees = {}", reader);
let writer = &mut greeting;
writer.push_str(", Rust!");
println!("greeting = {}", greeting);
Order is the whole trick. If the first println! came after the line that creates writer, the shared borrow would still be alive, and the compiler would reject the program with the E0502 error shown earlier. Printing greeting on the last line is only legal because writer is never used again.
Common mistakes
- Forgetting
muton the owner. You can't take&mutof an immutable binding. - Keeping a shared reference around. Reading through
readerafter creatingwriteris the classic conflict. - Taking a second
&mutwhile the first is still used later. - Using a value after moving it. Borrowing avoids this:
let u = &t;leavestusable, whilelet u = t;does not.
Key takeaways
- A reference borrows a value without taking ownership.
- Many
&or one&mut, never both at once. - A borrow ends at its last use, so ordering your reads and writes lets everything compile.
