Reference Operators - & and &mut
The reference operators & (shared reference) and &mut (mutable reference) create a borrow—a safe, non-null pointer that refers to an existing value in memory without taking ownership of it.
Shared (&T) vs Mutable (&mut T) References
Rust distinguishes two kinds of references at the type level:
| Operator | Reference Type | Name | Capabilities | Aliasing Rule (At Any Given Point) |
|---|---|---|---|---|
&expr |
&T |
Shared (immutable) reference | Read-only access to the target value (Copy—can be duplicated freely) |
Any number of &T references may coexist simultaneously, provided no &mut T is active |
&mut expr |
&mut T |
Mutable (exclusive) reference | Read and write access to the target value (not Copy—moves exclusively) |
At most one active &mut T may exist, and no &T may coexist with it |
Shared Borrowing (`&x`): Mutable Borrowing (`&mut x`):
+-------+ +-------+
| r1:&T | ──┐ | |
+-------+ ├──> +---------+ | r_mut | ──────> +---------+
| r2:&T | ──┘ | x: T | | &mut T| (Only 1)| x: T |
+-------+ +---------+ +-------+ +---------+
(Many readers, zero writers) (Single writer, zero other readers)
fn inspect_length(text: &String) -> usize {
text.len() // Reads `text` through `&String` without taking ownership
}
fn append_exclamation(text: &mut String) {
text.push('!'); // Modifies `text` in place through `&mut String`
}
fn main() {
let mut message = String::from("Rust");
// 1. Multiple shared references (`&String`) can coexist at the same time:
let ref_a: &String = &message;
let ref_b: &String = &message;
println!("Shared reads: '{ref_a}' and '{ref_b}' (len = {})", inspect_length(ref_a));
// `ref_a` and `ref_b` are never used after this line, so their borrows end here!
// 2. Exclusive mutable reference (`&mut String`) can now be created:
append_exclamation(&mut message);
println!("After mutation: '{message}'");
}
Shared reads: 'Rust' and 'Rust' (len = 4)
After mutation: 'Rust!'
Automatic Dereferencing with the Dot Operator (.)
When you access a struct field (r.field) or call a method (r.len()) on a reference &T or &mut T (or even nested references &&T), Rust's dot operator (.) automatically dereferences through as many layers of references as needed to reach the underlying value:
struct Point {
x: i32,
y: i32,
}
fn main() {
let pt = Point { x: 10, y: 20 };
let r1: &Point = &pt;
let r2: &&Point = &r1;
// Auto-dereferencing: `r2.x` is equivalent to `(**r2).x`!
println!("pt.x = {}, r1.x = {}, r2.x = {}", pt.x, r1.x, r2.x);
}
pt.x = 10, r1.x = 10, r2.x = 10
However, for arithmetic, comparison, or assignment directly on primitive values (*r = 5 or *r + 1), you must use the explicit dereference operator *.
Borrowing Temporary Values (Rvalue Temporary Promotion)
Normally, & and &mut are applied to named variables (&x). However, you can also apply & or &mut directly to a temporary value expression—such as &42 or &mut Vec::new().
When you take a reference to a temporary expression, Rust automatically creates an unnamed stack slot to hold the temporary value for the duration of the enclosing statement (or promotes constant values like &42 to static read-only memory 'static):
fn print_slice(data: &[i32]) {
println!("Slice: {data:?}");
}
fn main() {
// `&[1, 2, 3]` borrows a temporary array literal without needing a `let` variable:
print_slice(&[1, 2, 3]);
// `&42` is promoted to `&'static i32` in read-only memory:
let static_num: &'static i32 = &42;
println!("static_num = {static_num}");
}
Slice: [1, 2, 3]
static_num = 42
& in Expressions vs & in Patterns
Be careful to distinguish & as an expression operator (right-hand side of =) from & as a pattern (left-hand side of =, for, or match):
| Position | Syntax | Meaning |
|---|---|---|
| Expression (RHS) | let r = &x; |
Creates a reference (r has type &i32) |
| Pattern (LHS) | let &val = r; |
Matches and strips off a reference (val has type i32, copied out of r: &i32) |
fn main() {
let x: i32 = 99;
let r: &i32 = &x; // `&` in expression: creates a `&i32` reference
let &copied: &i32 = r; // `&` in pattern: destructures `&i32` so `copied` has type `i32`!
println!("copied = {copied}");
}
copied = 99
Common Compiler Errors
Violating Exclusivity: &mut While & Is Active (E0502)
If a shared reference r is created before a mutable borrow &mut and used again after the mutable borrow, the lifetimes overlap and the borrow checker rejects the program:
// ❌ does not compile
fn main() {
let mut value = 10;
let shared = &value;
let exclusive = &mut value;
println!("{shared} and {exclusive}");
}
error[E0502]: cannot borrow `value` as mutable because it is also borrowed as immutable
--> src/main.rs:4:21
|
3 | let shared = &value;
| ------ immutable borrow occurs here
4 | let exclusive = &mut value;
| ^^^^^^^^^^ mutable borrow occurs here
5 |
6 | println!("{shared} and {exclusive}");
| -------- immutable borrow later used here
error: aborting due to 1 previous error
Taking &mut of an Immutable Variable (E0596)
You cannot create a mutable reference (&mut x) unless x itself was declared with let mut:
// ❌ does not compile
fn main() {
let x = 5;
let r = &mut x;
}
error[E0596]: cannot borrow `x` as mutable, as it is not declared as mutable
--> src/main.rs:3:13
|
2 | let x = 5;
| - help: consider changing this to be mutable: `mut x`
3 | let r = &mut x;
| ^^^^^^ cannot borrow as mutable
error: aborting due to 1 previous error