& and * work and when to use each.In Rust, & and * show up everywhere. They’re easy to mix up at first.
What & does: references (borrowing)#
& creates a reference to a value. You’re not taking ownership or copying; you’re borrowing it.
let x = 42;
let r = &x; // r is a reference to x
rhas type&i32: “reference to i32”.rdoes not own the42; it points tox. Whenrgoes out of scope, nothing is dropped; only the reference disappears.
References are immutable by default. To allow changes through the reference, you need a mutable reference &mut:
let mut x = 42;
let r = &mut x;
*r += 1; // we need * to get at the value (see below)
You can have many &T references at once, or one &mut T, following Rustâs borrowing rules.
What * does: dereferencing#
* dereferences a reference: it follows the pointer and gives you the value it points to.
let x = 42;
let r = &x;
assert_eq!(*r, 42); // *r is the value 42
So:
&â âtake a reference to thisâ*â âgive me the value this reference points toâ
Without *, youâre working with the reference (the âpointerâ); with *, youâre working with the underlying value.
When do you need *?#
When the type doesnât match, the compiler often forces you to dereference.
fn add_one(n: &i32) -> i32 {
*n + 1 // *n gives i32 so we can add 1
}Here n is &i32. The expression n + 1 would be invalid (reference + integer). So you write *n + 1 to get an i32 and then add 1.
Method calls usually donât need *. Rustâs âmethod receiver deref coercionâ applies dereferences for you:
let s = String::from("hello");
let r = &s;
println!("{}", r.len()); // no * needed; r is auto-dereferenced
So you use * when you need the value as a value (e.g. in expressions, assignments, or when passing to a function that expects T and you have &T).
Putting it together#
| Symbol | Name | Meaning |
|---|---|---|
&x | Reference | Borrow x; type is &T |
&mut x | Mutable ref | Borrow x mutably; type is &mut T |
*r | Dereference | The value r points to; type is T |
Example that uses both:
fn double(n: &mut i32) {
*n *= 2; // dereference to read and write the value
}
let mut x = 5;
double(&mut x); // pass a mutable reference
assert_eq!(x, 10);&mut xcreates a mutable reference tox.- Inside
double,*nis the actuali32, so*n *= 2modifies the value in place.
Summary#
&: create a reference (borrow). Types like&T,&mut T, or when callingfoo(&x).*: dereference. Follow the reference to get the value. Use it when you need aTand you have&Tor&mut T.
Once you read & as âreference toâ and * as âvalue pointed to,â the rest is mostly the borrow checker and a bit of practice.
Box, Rc, and RefCell in a later post: other ways to own or share data without raw pointers.