Immutable References
An immutable reference (also called a shared reference), written &T, is a read-only pointer that borrows a value of type T without taking ownership of it. Multiple immutable references to the same value can exist and be used simultaneously, provided no mutable reference (&mut T) to that value is active at the same time.
Creating and Using an Immutable Reference
Apply the & operator to an expression to create an immutable reference &T:
fn count_words(text: &String) -> usize {
text.split_whitespace().count()
}
fn main() {
let document = String::from("Immutable references allow shared read access.");
// Create two immutable references borrowing `document` at the same time:
let reader_a: &String = &document;
let reader_b: &String = &document;
println!("Word count via reader_a: {}", count_words(reader_a));
println!("Direct read via reader_b: {reader_b}");
println!("Original owner still intact: {document}");
}
Word count via reader_a: 6
Direct read via reader_b: Immutable references allow shared read access.
Original owner still intact: Immutable references allow shared read access.
Why &T Is Called a "Shared Reference"
In Rust documentation and compiler messages, &T is referred to as both an immutable reference and a shared reference:
- Immutable: You cannot modify the target value through
&T(*r = new_valis forbidden). Furthermore, as long as any&Treference is alive, even alet mutowner cannot modify or move the value! - Shared: Unlike
&mut T(which is exclusive and cannot be copied), a shared reference&Timplements theCopytrait—for every typeT, even whenTitself is notCopy(such as&Stringor&Vec<u8>)!
Because `&T` implements `Copy` for ALL `T`:
let s = String::from("heap"); // `String` is NOT Copy
let r1: &String = &s; // `&String` IS Copy! (8-byte pointer)
let r2: &String = r1; // Copies the 8-byte pointer; both `r1` and `r2` remain valid!
fn main() {
let owned_vec = vec![10, 20, 30];
let r1: &Vec<i32> = &owned_vec;
let r2: &Vec<i32> = r1; // Copies the 8-byte reference `r1` without moving it!
println!("r1 = {r1:?}, r2 = {r2:?}");
}
r1 = [10, 20, 30], r2 = [10, 20, 30]
Taking &T of an Immutable vs Mutable Variable
You can create an immutable reference &T from either an immutable variable (let x) or a mutable variable (let mut y). When you borrow a mutable variable y as &y, y is temporarily frozen (read-only) for the duration of the immutable borrow:
fn main() {
let mut counter = 10;
// Borrow `counter` immutably:
let snap: &i32 = &counter;
println!("Snapshot while frozen: {snap}");
// `snap` is not used after this line, so the immutable borrow ends here!
// Now `counter` can be mutated again:
counter += 5;
println!("Updated counter: {counter}");
}
Snapshot while frozen: 10
Updated counter: 15
Common Compiler Errors with &T
1. Attempting to Mutate Through an Immutable Reference (E0594 / E0596)
Calling a mutating method (.push(), .clear()) or assigning *r = ... through &T is rejected at compile time:
// ❌ does not compile
fn try_append(list: &Vec<i32>) {
list.push(4);
}
fn main() {
let mut v = vec![1, 2, 3];
try_append(&v);
}
error[E0596]: cannot borrow `*list` as mutable, as it is behind a `&` reference
--> src/main.rs:2:5
|
2 | list.push(4);
| ^^^^ `list` is a `&` reference, so the data it refers to cannot be borrowed as mutable
|
help: consider changing this to be a mutable reference
|
1 | fn try_append(list: &mut Vec<i32>) {
| +++
error: aborting due to 1 previous error
2. Mutating or Moving the Owner While an Immutable Borrow Is Active (E0502 / E0505)
As long as an immutable reference r is still going to be used on a later line, the owner variable cannot be mutated (E0502) or moved out of (E0505):
// ❌ does not compile
fn main() {
let mut name = String::from("Ferris");
let borrowed: &String = &name;
name = String::from("Replaced"); // Error: `name` is currently borrowed by `borrowed`!
println!("{borrowed}");
}
error[E0506]: cannot assign to `name` because it is borrowed
--> src/main.rs:5:5
|
3 | let borrowed: &String = &name;
| ----- `name` is borrowed here
4 |
5 | name = String::from("Replaced");
| ^^^^ `name` is assigned to here but it was already borrowed
6 |
7 | println!("{borrowed}");
| ---------- borrow later used here
error: aborting due to 1 previous error