One topic that is causing a lot of confusion for Rust new-joiners is a difference between immutable/mutable variables and immutable/mutable references types.
We are going to break down each below case and consider it separately, with examples:
- Immutable variable bound to a value (
let t: T = v
) - Mutable variable bound to a value (
let mut t: T = v
) - Immutable variable bound to Immutable reference (
let t: &T = &v
) - Immutable variable bound to Mutable reference (
let t: &mut T = &mut v
) - Mutable variable bound to Immutable reference (
let mut t: &T = &v
) - Mutable variable bound to Mutable reference (
let mut t: &mut T = &mut v
)
Immutable variable bound to a value (let t: T = v
)
// [Example 1]:
let t: i32 = 1;
t = 2; //compilation error: cannot assign twice to immutable variable `t1`
// [Example 2]:
struct A { a: i32}
let t1: A = A {a:1};
t1 = A {a:2}; //compilation error: cannot assign twice to immutable variable `t`
t1.a = 3; //compilation error: cannot assign to `t1.a`, as `t1` is not declared as mutable
In the first example, a variable t
was declared as immutable (was not explicitly marked as mutable), so it was not possible to re-bind it to a new value 2
. Similarly, in the second example, it was not possible to re-bind an immutable variable t1
to a new structure value A {a: 2}
. And also, since t1
was declared immutable, it was not possible to update a structure field t1.a
to a new value 3
.
Mutable variable bound to a value (let mut t: T = v
)
// [Example 1]:
let mut t: i32 = 1;
t = 2; //compiles OK
// [Example 2]:
struct A { a: i32 }
let mut t1: A = A {a:1};
t1 = A {a:2}; //compiles OK
t1.a = 3; //compiles OK
In the first example, a variable t
was declared as mutable, so it was possible to re-bind it to a new value 2
. Similarly, in the second example, a mutable variable t1
was bound to a structure value A {a:1}
, and then it was possible to re-bind the variable to a new structure value A {a:2}
. Also, it was possible to mutate the field t1.a
inside the structure to a new value 3
.
Immutable variable bound to Immutable reference (let t: &T = &v
)
let t: &i32 = &1;
t = &2; //compilation error: cannot assign twice to immutable variable `t`
*t = 2; //compilation error: cannot assign to `*t`, which is behind a `&` reference
A variable t
was declared as immutable, so it was not possible to re-bind it to a new immutable reference &2
(a new immutable reference pointing to another value 2
). Also it was not possible to update the actual referent value *t
via the reference t
, because the reference t
was itself defined as immutable &i32
.
Immutable variable bound to Mutable reference (let t: &mut T = &mut v
)
let t: &mut i32 = &mut 1;
t = &mut 2; //compilation error: cannot assign twice to immutable variable `t`
*t = 2; //compiles OK
A variable t
was defined as immutable (even though it was bound to a mutable reference &mut 1
), so it was not possible to re-bind it to a new mutable reference &mut 2
(a new mutable reference pointing to another value 2
). But it was OK to update the actual referent value *t
via the reference t
, because the reference t
was itself defined as mutable &mut i32
.
Mutable variable bound to Immutable reference (let mut t: &T = &v
)
let mut t: &i32 = &1;
t = &2; //compiles OK
*t = 2; //compilation error: cannot assign to `*t`, which is behind a `&` reference
A variable t
was declared as mutable, so it was possible to re-bind it to a new immutable reference &2
(a new immutable reference pointing to another value 2
). But it was not possible to update the actual referent value *t
via the reference t
, because the reference t
was itself defined as immutable &i32
.
Mutable variable bound to Mutable reference (let mut t: &mut T = &mut v
)
let mut t: &mut i32 = &mut 1;
let new_t: &mut i32 = &mut 2;
t = new_t; //compiles OK
*t = 2; //compiles OK
A variable t
was declared as mutable, so it was possible to re-bind it to a new mutable reference &mut 2
(a new mutable reference pointing to another value 2
). In this case, we had to bind &mut 2
to another variable new_t
first, because assigning it directly to t
would cause a compilation error temporary value is freed at the end of this statement
. Also it was OK to update the actual referent value *t
via the reference t
, because the reference t
was itself defined as mutable &mut i32
.