Working Code
In the previous lesson you learned that passing a value to a function moves ownership. clone() works, but copying every time is wasteful.
There's a better way — borrowing!
fn print_message(msg: &String) {
// msg is borrowed — read-only access
println!("{msg}");
}
fn main() {
let message = String::from("Hello!");
print_message(&message); // lend it out
println!("Still usable: {message}"); // owner unchanged!
}
The & is the key. &message means "I'm lending this value," and msg: &String means "I'm receiving a borrowed value."
Try It Yourself
- Fix the error-prone code from Lesson 09 using borrowing:
fn print_fruits(fruits: Vec<&str>) {
for f in fruits {
println!("{f}");
}
}
fn main() {
let fruits = vec!["apple", "banana"];
print_fruits(fruits);
println!("Number of fruits: {}", fruits.len()); // error!
}
Hint: Add & to the function parameter and the call site.
- Can you get the same result without
clone()? Try it!
"Why?" — Think of It Like a Library Loan
Borrowing is easier to understand as a library loan:
- You borrow a book (value) from the library (owner), but the library remains the owner
- You can read the book, but you can't tear out pages (immutable borrow)
- When the loan period is over, you return it
fn calculate_length(s: &String) -> usize {
s.len()
// the borrowed reference is returned when the function ends
}
fn main() {
let word = String::from("Rust");
let len = calculate_length(&word);
println!("Length of {word}: {len}");
}
Because you lent with &word, ownership didn't move. The borrowed reference is automatically returned when the function ends.
Mutable Borrowing — Edit Permissions
What if you need to modify a value, not just read it? Use &mut.
fn add_exclamation(s: &mut String) {
s.push_str("!!!");
}
fn main() {
let mut message = String::from("hello");
add_exclamation(&mut message);
println!("{message}"); // "hello!!!"
}
Think of mutable borrowing as edit permissions:
- Read permission (
&): Multiple people can have it at the same time - Edit permission (
&mut): Only one person can have it at a time
Why is editing limited to one? If multiple people edit the same document simultaneously, the content gets corrupted. Rust prevents this at compile time.
Borrowing Rules Summary
- You can have multiple immutable references (
&) at the same time- You can have only one mutable reference (
&mut) at a time- Immutable and mutable references cannot coexist
fn main() {
let mut data = String::from("hello");
let r1 = &data; // OK — read permission 1
let r2 = &data; // OK — read permission 2
println!("{r1}, {r2}");
let r3 = &mut data; // OK — the read permissions are no longer in use
r3.push_str(" world");
println!("{r3}");
}
error[E0502]: cannot borrow `data` as mutable because it is
also borrowed as immutable
--> src/main.rs:6:14
|
4 | let r1 = &data;
| ----- immutable borrow occurs here
5 | let r2 = &data;
6 | let r3 = &mut data;
| ^^^^^^^^^ mutable borrow occurs here
7 | println!("{r1}, {r2}");
| --- immutable borrow later used here
What it means: "You borrowed data immutably and then tried to borrow it mutably at the same time. You can't grant edit access while read access is still in use."
Fix: Create the mutable reference after you're done using the immutable ones.
Learn More: NLL — How Rust Got Smarter
Since Rust 2018, NLL (Non-Lexical Lifetimes) treats a reference as valid only until its last point of use.
fn main() {
let mut data = String::from("hello");
let r1 = &data;
println!("{r1}"); // last use of r1
let r2 = &mut data; // OK! r1 is no longer in use
r2.push_str(" world");
println!("{r2}");
}
This used to be an error, but now the compiler recognizes that "r1 isn't used anymore" and allows the mutable borrow.
Exercise 1 (easy). Fix the code below using borrowing.
fn greet(name: String) {
println!("Hello, {name}!");
}
fn main() {
let name = String::from("Alice");
greet(name);
greet(name); // error! name was moved
}
Hint: Use & so the function doesn't take ownership.
Exercise 2 (medium). Fix the error in this code.
fn main() {
let mut numbers = vec![1, 2, 3];
let first = &numbers[0];
numbers.push(4);
println!("First: {first}");
}
Hint: numbers.push(4) requires a mutable borrow of the vector. What if you use first before calling push?
Exercise 3 (challenge). Complete the function below. It should return the first word of a string.
fn first_word(s: &String) -> &str {
// Complete this!
// Hint: use s.find(' ') and &s[..index]
}
fn main() {
let sentence = String::from("hello world");
let word = first_word(&sentence);
println!("First word: {word}");
}
Q1. What does the & symbol mean?
- A) It copies the value
- B) It creates a reference (borrow) to the value
- C) It deletes the value
- D) It changes the type of the value
Q2. Why does this code produce an error?
fn main() {
let mut s = String::from("hello");
let r1 = &s;
let r2 = &mut s;
println!("{r1}");
}
- A)
Stringcannot be borrowed - B) An immutable and a mutable reference exist at the same time
- C)
mutwas used twice - D) You can't pass a reference to
println!
Q3. How many mutable references (&mut) can exist at the same time?
- A) Unlimited
- B) 2
- C) 1
- D) 0 (they can't be created)