Dereference Operator
The unary prefix dereference operator (*) follows a reference (&T, &mut T), smart pointer (Box<T>), or raw pointer (*const T, *mut T) to access the underlying memory location (place) that the pointer points to.
Following References with *
When a variable r holds a reference &i32 or &mut i32, r itself stores a memory address, while *r refers to the i32 value at that address:
Stack
+---------------+ +---------------+
| r: &mut i32 | ------> | x: i32 = 10 |
| (holds addr) | | (*r accesses |
+---------------+ | this slot) |
+---------------+
fn main() {
let mut x: i32 = 10;
// 1. Read through a shared reference `&i32` using `*`:
let r_shared: &i32 = &x;
assert_eq!(*r_shared, 10);
println!("Read via *r_shared: {}", *r_shared);
// 2. Write (mutate) through a mutable reference `&mut i32` using `*`:
let r_mut: &mut i32 = &mut x;
*r_mut += 25;
println!("Mutated via *r_mut: {}", *r_mut);
println!("Final x = {x}");
}
Read via *r_shared: 10
Mutated via *r_mut: 35
Final x = 35
Multi-Level Dereferencing (**r)
Each * strips away one layer of indirection. If you have a reference to a reference (&&T), *r has type &T, and **r reaches the underlying T:
fn main() {
let value: i32 = 42;
let level_1: &i32 = &value;
let level_2: &&i32 = &level_1;
println!("*level_1 = {}", *level_1);
println!("**level_2 = {}", **level_2);
}
*level_1 = 42
**level_2 = 42
When Explicit * Is Required vs Automatic Dereferencing
Because the dot operator (.) for method calls and field access automatically dereferences its receiver (r.len() becomes (*r).len()), you do not need * for method calls or struct field access.
However, you must write explicit * in three common situations:
| Situation | Why Explicit * Is Required |
Example |
|---|---|---|
Assigning a new value to the target of &mut T |
Writing r = val would try to reassign the reference variable r itself instead of the target memory |
*r = 100; or *r += 1; |
| Arithmetic or bitwise operations on a referenced primitive | Binary operators like + or * on &i32 + i32 or &mut i32 require the value |
let sum = *r + 10; |
Copying a Copy value out of &T to own it |
Converts &T into an owned T |
let owned: i32 = *r; |
fn double_in_place(slot: &mut i32) {
// Explicit `*` is required on both sides:
*slot = *slot * 2;
}
fn main() {
let mut n = 21;
double_in_place(&mut n);
println!("n = {n}");
}
n = 42
Note on Operator Precedence: The dot operator (
.) has higher precedence than unary*! That means*wrapper.valueis parsed as*(wrapper.value). Ifwrapperis a reference to a struct whose field is not a pointer,wrapper.valueauto-dereferenceswrapperautomatically; if you ever need to dereference the receiver explicitly before accessing a tuple index or method, wrap it in parentheses(*r).0.
Ownership Rules When Dereferencing (E0507)
When you dereference a reference *r on the right-hand side of an assignment (let val = *r;):
- If
TimplementsCopy(such asi32,f64,bool,char),*rcopies the bytes out of the target intoval. - If
Tdoes not implementCopy(such asStringorVec<T>),let val = *r;attempts to move ownership of the target value out from behind a borrowed reference! Because a reference (&Tor&mut T) does not own the target, Rust forbids moving a non-Copyvalue out of a reference withE0507:
// ❌ does not compile
fn main() {
let text = String::from("owned string");
let r: &String = &text;
let stolen: String = *r;
}
error[E0507]: cannot move out of `*r` which is behind a shared reference
--> src/main.rs:5:26
|
5 | let stolen: String = *r;
| ^^ move occurs because `*r` has type `String`, which does not implement the `Copy` trait
|
help: consider cloning the value if the performance cost is acceptable
|
5 - let stolen: String = *r;
5 + let stolen: String = r.clone();
error: aborting due to 1 previous error
To obtain an owned value from &String or &mut String, either call r.clone() (to make a copy) or, if you have a &mut String and want to take its contents while leaving an empty String behind, use std::mem::take(r) or std::mem::replace(r, new_val).
Custom Smart Pointers and the Deref Trait
Beyond built-in references (&T and &mut T), the * operator also works on smart pointers such as Box<T>, Rc<T>, Arc<T>, and MutexGuard<T>. For any type that implements std::ops::Deref (and DerefMut for mutable dereferencing), writing *smart_ptr invokes *(smart_ptr.deref()):
fn main() {
let heap_num: Box<i32> = Box::new(50);
// `*heap_num` follows the heap pointer inside `Box<i32>` to read `50`:
assert_eq!(*heap_num, 50);
println!("Dereferenced Box: {}", *heap_num);
}
Dereferenced Box: 50