In our previous example, m which had the value &n is the simplest form of pointer, called a reference (or borrowed pointer)--the variable m is a reference to the stack-allocated variable n and has type &i32 because it points to a value of type i32.
Here n is immutable, so m is also immutable; trying to change the value of n through m with *m = 7; for example, results in an error:
cannot assign to immutable borrowed content `*m`
Rust does not let you change an immutable variable via its pointer, contrary to C.
Because there is no danger of changing the value of n through a reference, multiple references to an immutable value are allowed, they can only be used to read the value, for example:
let o = &n; println!("The address of n is {:p}", o); println!("The value of n is {}", *o);
Printing out as before:
The address of n is 0x7078eff8bc The value of n is 42
We could represent this situation in memory like this:

It will be clear that working with pointers like this or in much more complex situations necessitates much stricter rules than the Copy behavior. For example, the memory can only be freed when there are no variables or pointers associated with it anymore. And when the value is mutable, can it be changed through any of its pointers? These stricter rules, described by the ownership and borrowing system from the next section, are enforced by the compiler.
Mutable references do exist, and they are declared as:
let m = &mut n
However the variable n also has to be a mutable value. When n is immutable, the compiler rejects the m mutable reference binding with the error:
error: cannot borrow immutable local variable `n` as mutable
This makes sense, as immutable variables cannot be changed, even when you know their memory location.
To reiterate, in order to change a value through a reference, both the variable and its reference have to be mutable, like in the following code snippet:
let mut u = 3.14f64; let v = &mut u; *v = 3.15; println!("The value of u is now {}", *v);
This will print the following output:
The value of u is now 3.15 Now the value at the memory location of u is changed to 3.15
But note that we now cannot change (or even print) that value anymore by using the variable u:
A statement like u = u * 2.0; or even a simple println!("The value of u is {}", u); give us respectively the compiler error:
errors: cannot assign to `u` because it is borrowed
(We explain why this is so in the following section on Ownership and Borrowing)
cannot borrow `u` as immutable because it is also borrowed as mutable
We say that borrowing a variable (by taking a reference to it, here v) freezes that variable, the original variable u is frozen (no longer usable), until the v reference goes out of scope.
Also we can only have one mutable reference: let w = &mut u; which results in the following error:
error: cannot borrow `u` as mutable more than once at a time
The compiler even adds the following note to the previous code line with: let v = &mut u; which results in the following error:
note: previous borrow of `u` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `u` until the borrow ends.
This is logical--the compiler is (rightfully) concerned that a change to the value of u through one reference might change its memory location, because the variable u might change in size (in case of a more complex variable than an integer) and so not fit anymore in its previous location, and would have to be relocated to another address. This would render all other references to u invalid, and even dangerous, because through them we might inadvertently change another variable that has taken up the u variable's previous location!
A mutable value can also be changed by passing its address as a mutable reference to a function, like in this example:
let mut m = 7; add_three_to_magic(&mut m); println!("m is now {}", m); // prints out m is now 10
With the function add_three_to_magic declared as:
fn add_three_to_magic(num: &mut i32) { *num += 3; // value is changed in place through += }
References are frequently used as function parameters: they avoid moving (which means in most cases copying) the value to the function, which could decrease performance when the value is large. Instead passing a reference only copies the address of the value to the function, not the value, thereby saving unnecessary memory usage. Consider a string buffer that contains a large amount of data. Copying that around will cause the program to be much slower.