Operators and Expressions

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