References & Borrowing
Prerequisites: Ownership
Moving values into functions and handing them back gets tedious. Instead, you can borrow a value with a reference: a pointer that lets code use a value without taking ownership of it.
Syntax
let s = String::from("hi");
let r = &s; // shared (immutable) reference
let mut t = String::from("hi");
let m = &mut t; // mutable reference
m.push('!');
&valuecreates a shared reference. You can read through it, but not change the value.&mut valuecreates a mutable reference. You can change the value through it. The variable itself must be declaredmut.- A function parameter of type
&Stringor&mut Stringborrows the argument. When the function returns, the borrow ends and the caller still owns the value.
The borrowing rules
At any given time, you can have either:
- one mutable reference, or
- any number of shared references,
but not both. And references must always be valid — a reference can never outlive the value it points to.
These rules prevent data races and bugs where data changes while someone else is reading it. A borrow lasts from where it's created to where it's last used, not necessarily to the end of the scope — so once you've finished with shared references, you can create a mutable one.
Example
fn main() {
let s = String::from("hello");
let len = calculate_length(&s); // borrow `s`
println!("'{s}' has length {len}"); // `s` is still ours
let mut greeting = String::from("Hello");
add_world(&mut greeting);
println!("{greeting}");
let r1 = &greeting;
let r2 = &greeting; // many shared references: fine
println!("{r1} / {r2}");
// r1 and r2 aren't used after this line, so their borrows end
let r3 = &mut greeting; // now a mutable reference is allowed
r3.push('!');
println!("{r3}");
}
fn calculate_length(text: &String) -> usize {
text.len()
}
fn add_world(text: &mut String) {
text.push_str(", world");
}
(&String parameters keep this example simple. The next lesson shows
why &str is the more flexible choice for text parameters.)
Expected output
'hello' has length 5
Hello, world
Hello, world / Hello, world
Hello, world!
Common mistakes
- Mixing a shared and a mutable borrow:
let mut s = String::from("hi");
let r1 = &s;
let r2 = &mut s;
r2.push('!');
println!("{r1}");
error[E0502]: cannot borrow `s` as mutable because it is also borrowed as immutable
r1 is still used on the last line, so its borrow is still active
when r2 is created. Finish using r1 first.
- Two mutable borrows at once:
error[E0499]: cannot borrow `s` as mutable more than once at a time
- Returning a reference to a local variable. The local is dropped when the function ends, so the reference would dangle:
fn dangle() -> &String {
let s = String::from("hello");
&s
}
error[E0106]: missing lifetime specifier
Return the owned String instead.
Practice
Write a function shout that takes a &mut String and appends
"!!!" to it. Call it on a String holding "hey", then print the
result: hey!!!.
Show solution
fn shout(text: &mut String) {
text.push_str("!!!");
}
fn main() {
let mut message = String::from("hey");
shout(&mut message);
println!("{message}");
}