The second principle of memory in Rust is borrowing. Instead of giving the ownership of a value to another part of the code, we give a reference to it. We may give:
Either one or more immutable references to other parts of our code.
Or only one mutable reference to another part of our code.
19.2.1Defining a reference¶
Here is the definition of a reference on an integer variable:
{
let a = 16;
let b = &a;
println!("{b}");
}16
()19.2.2Dereferencing¶
Most of the time a reference cannot be used as is. The following
equality test cannot compile because a is a i32, while b is a
&i32:
let a = 16;
let b = &a;
16 == b[E0277] Error: can't compare `{integer}` with `&{integer}`
╭─[ command_3:1:1 ]
│
3 │ 16 == b
│ ─┬ │
│ ╰─── no implementation for `{integer} == &{integer}`
│ │
│ ╰─ help: consider dereferencing here: `*`
───╯The compiler gives us the solution to make it work. We need to use
the dereferencing operator * that will convert back the &i32
into a i32:
{
let a = 16;
let b = &a;
println!("{}", 16 == *b);
}true
() ┌────────────────┐ ┌────────┐
│ b (0xa413b...) │────────>│ a (16) │
└────────────────┘ └────────┘The type of b is thus a reference on an integer, but it is also a
regular variable. As a consequence, we can also make a reference on
b. Let us define c a variable that is a reference on b:
┌────────────────┐ ┌────────────────┐ ┌────────┐
│ c (0x9c1f7...) │────────>│ b (0xa413b...) │────────>│ a (16) │
└────────────────┘ └────────────────┘ └────────┘To test c against b we need to dereference it. To test c
against a’s value we need to dereference it twice:
{
let a = 16;
let b = &a;
let c = &b;
println!("{}", b == *c);
println!("{}", 16 == **c);
}true
true
()19.2.3Passing a reference¶
When passing a value by reference to a function, we need to declare the reference both in the function definition and the function call:
fn foo(x: &i32) {
println!("{x}");
}
let a = 16;
foo(&a);16
If we want to access the referenced value directly, we need to use
the dereferencing operator *:
fn foo(x: &i32) -> i32 {
*x + 2
}
let a = 16;
foo(&a)1819.2.4The ref pattern¶
The ref keyword can be used in place of the & operator.
For instance when defining a new variable:
{
let a = 10;
let ref b = a;
println!("{:?}", (a, b, a == *b));
}(10, 10, true)
()If we define a function that takes a reference, we see that we can
pass b directly to the function, since it is already a reference:
{
let a = 10;
let ref b = a;
fn foo(x: &i32) -> i32 {
*x + 2
}
println!("{}", foo(b));
}12
()The ref keyword is useful when writing match expressions, like in
if let. Let us define an Option value, and write a match using
the ref keyword to make the variable n a reference on the
internal value of a instead of a copy of the value:
{
let a = Some(13);
fn foo(x: &i32) -> i32 {
*x + 2
}
if let Some(ref n) = a {
println!("{}", foo(n));
}
}15
()If we have a mutable Option value, we can get a mutable reference
on the internal value using ref and mut together. This allows us
to modify the internal value, which we can check afterward:
{
let mut b = Some(20);
if let Some(ref mut n) = b {
*n += 5;
}
println!("{:?}", b);
}Some(25)
()19.2.5Indexing a vector¶
When indexing a vector of a non-copiable type (i.e.: a type that does
not implement the Copy trait) using the [] operator, the values have
to be moved out of the collection (i.e.: the ownership passes from the
collection to the caller). Hopefully, the compiler forbids this usage of
the [] operator.
19.2.6Mutable reference¶
If a variable is mutable, we may define a mutable reference on it in order to be able to modify the value:
{
let mut a = 16;
let b = &mut a;
*b = 20;
println!("{b}");
}20
()When passing a mutable reference to a function, both references in the function call and in the function definition must be declared mutable:
fn foo(s: &mut String) {
s.push_str("def");
}
{
let mut s = String::from("abc");
foo(&mut s);
s
}"abcdef"19.2.7Borrowed mutable variable¶
While borrowed, a mutable variable is not freely accessible. Only after the reference’s scope termination will we get back full access to the original variable.
When a mutable variable is immutably borrowed, we may access its value and use it:
let mut a = 16;
{
let b = &a;
let c = a + 5;
println!("(b, c)={:?}", (*b, c));
}
a = 8;
a(b, c)=(16, 21)
8But we cannot modify it:
let mut a = 16;
let b = &a;
a = 8;
(a, b)[E0506] Error: cannot assign to `a` because it is borrowed
╭─[ command_15:1:1 ]
│
2 │ let b = &a;
│ ─┬
│ ╰── `a` is borrowed here
3 │ a = 8;
│ ──┬──
│ ╰──── `a` is assigned to here but it was already borrowed
│
│ Note: note: requirement that the value outlives `'static` introduced here
───╯When a mutable variable is mutably borrowed, we may neither access nor modify its value:
let mut a = 16;
let b = &mut a;
let c = a + 5;
(b, c)[E0503] Error: cannot use `a` because it was mutably borrowed
╭─[ command_16:1:1 ]
│
2 │ let b = &mut a;
│ ───┬──
│ ╰──── `a` is borrowed here
3 │ let c = a + 5;
│ ┬
│ ╰── use of borrowed `a`
4 │ (b, c)
│ ┬
│ ╰── borrow later used here
───╯19.2.8Multiple references¶
As said earlier, multiple immutable references on the same variable
are allowed:
{
let s = 8;
let t = &s;
let u = &s;
println!("{:?}", (s, t, u));
}(8, 8, 8)
()However it is forbidden to define multiple mutable references on the
same variable:
let mut s = 8;
let t = &mut s;
let u = &mut s;
(t, u)[E0499] Error: cannot borrow `s` as mutable more than once at a time
╭─[ command_18:1:1 ]
│
2 │ let t = &mut s;
│ ───┬──
│ ╰──── first mutable borrow occurs here
3 │ let u = &mut s;
│ ───┬──
│ ╰──── second mutable borrow occurs here
4 │ (t, u)
│ ┬
│ ╰── first borrow later used here
───╯Or to define a mutable reference while at least one immutable
reference exists:
{
let mut s = 8;
let t = &s;
let u = &mut s; // t is still valid since it is used on the line after
println!("{:?}", (t, u));
}[E0502] Error: cannot borrow `s` as mutable because it is also borrowed as immutable
╭─[ command_19:1:1 ]
│
4 │ let u = &mut s; // t is still valid since it is used on the line after
│ ───┬──
│ ╰──── mutable borrow occurs here
│
├─[ command_19:1:1 ]
│
3 │ let t = &s;
│ ─┬
│ ╰── immutable borrow occurs here
│
5 │ println!("{:?}", (t, u));
│ ┬
│ ╰── immutable borrow later used here
───╯To solve the issue we need to terminate the scope of t earlier in the
code (i.e.: before u is created):
{
let mut s = 8;
let t = &s;
println!("{t}"); // End of t's scope
let u = &mut s;
println!("{u}");
}8
8
()19.2.9Borrowing blocks moving¶
In the same way borrowing blocks modifications on a mutable variable, it blocks also the moving of ownership.
In the following example, s cannot be moved, because the reference
t is still alive:
{
let s = String::from("abc");
let t = &s;
println!("{:?}", (s, t));
}[E0505] Error: cannot move out of `s` because it is borrowed
╭─[ command_21:1:1 ]
│
4 │ println!("{:?}", (s, t));
│ ┬
│ ╰── move out of `s` occurs here
│
├─[ command_21:1:1 ]
│
3 │ let t = &s;
│ ─┬
│ ╰── borrow of `s` occurs here
│
├─[ command_21:1:1 ]
│
2 │ let s = String::from("abc");
│ ┬
│ ╰── binding `s` declared here
│
4 │ println!("{:?}", (s, t));
│ ┬
│ ╰── borrow later used here
│
├─[ command_21:1:1 ]
│
3 │ let t = &s;
│ │
│ ╰─ help: consider cloning the value if the performance cost is acceptable: `.clone()`
───╯