Even though Rust provides its Rust book, I struggled with understanding the mut
keyword. I asked myself, "Am I the only one who has this problem?" A quick Google search confirmed that I was not alone.
As a result, I decided to write this article to provide a detailed explanation of the mut
keyword. This article is intended for those who come from high-level languages such as Python or JavaScript.
To create an unmuttable variable in Rust, simply write let x = 1337
. It's straightforward. If you want to create a variable that can be mutated later, just add the mut
keyword after let
. Rust has a helpful convention that encourages clarity of intentions.
Adding the mut
keyword informs others that this variable will be modified somewhere else in the code. Okay.
Let's visualize it. Two variables here, let mut x = 1337
and let y = 42
.
At the moment, everything is straightforward. However, things start to get a little curly when using mut
references. Let's create some.
let mut x = 1337;
let y = 42;
let x_ref = &mut x;
let y_ref = &y;
I created two references (or "borrowed" in terms of Rust). One of them is a mutable reference, and the other is a read-only reference. Let's create a scheme for that again.
In the given scheme, I have 4 variables, 2 of which are references. Both reference variables are immutable and do not have the mut
keyword after let
, which means I cannot change what they point to. However, I can still change the value they reference.
*x_ref = 777;
If you write this, the Rust compiler will not complain, and the value of x
(not the ref itself) will change to 777
. However, there's a red square on the scheme indicating that x_ref
lacks the mutability option. So, why can I change the value it refers to?
Let’s come back to the scheme for the let x_ref = &mut x
.
The first white block contains the name: x_ref
. The second one informs me about the type stored in that variable. In its complete form, without any implicit type annotations, I can write as follows:
let x_ref: &mut i32 = &mut x;
I can interpret this as: let's create an immutable variable named x_ref
that will hold a mutable reference to i32
and initialize it immediately with the mutable reference to the i32
value in the x
variable.
This means I can modify the value it points to, but I cannot alter the reference's value (or address). In other words, I can't write something like:
let x_ref: &mut i32 = &mut x;
let mut z = 0;
x_ref = &mut z; // Not allowed!
In terms of the schemes, I want to change the direction the arrow is pointing to in the code block above. However, even if the z
variable is mutable, I can't change the arrow because the problem lies in the immutability of the x_ref
itself.
To change the arrow direction, I need to modify the address stored in the x_ref
variable. However, I cannot do this as the variable is immutable.
Let’s do it!
let mut x: i32 = 1337;
let mut x_ref: &mut i32 = &mut x; // I've added mut before x_ref
let mut z = 0;
x_ref = &mut z; // Allowed!
There are too many instances of mut
here around x_ref
, right? Let's describe them.
let mut x_ref
: I am creating a mutable variable named x_ref
, which means I can change its value later.
&mut i32
: I am stating that the variable will contain mutable reference(s) to some value of type i32
.
&mut x
: I am borrowing (getting a reference to) the variable x
.
Then, I created a variable named z
and assigned it the value of 0
. Afterward, when I wrote x_ref = &mut z
, I indicated that I understand x_ref
to be a mutable variable that can only hold references to i32
values.
Since the type of z
is i32
, I am able to assign its address to the x_ref
variable. To obtain the address of z
, I used the &mut z
syntax.
The scheme.
Take a look at =
in the statement, it may look a little bit obvious, but…
let mut x_ref = &mut x;
… I see it as a divider (especially if you rotate it by 90 degrees) that splits the statement into two sub-statements: left and right. The left side provides information about the variable itself, while the right side tells us about the value.
When I use the *
dereference operator to change the value...
*x_ref = 100;
... I do not change the value of the x_ref
variable. Instead, I am changing the value that x_ref
is referencing.
I used mut
frequently before. What if I omit some of them?
let i = 1;
let j = 2;
let mut k = &i;
Can I change the value of i
here? Using the divider technique, it's quite simple to answer. I can change the value of k
(I see mut
on the left side), but the value (right side) is an immutable reference to i
(there is no mut
here).
Therefore…
let i = 1;
let j = 2;
let mut k = &i;
k = &j; // This is legal.
*k = 3; // This is not.
The scheme.
In this article, we've dissected the nuances of the mut
keyword and references. Remember, there's a distinction between a mutable reference and a mutable variable holding a reference. Our trick?
Using the =
sign as a mental divider to better understand assignments in Rust. This simple visualization can clear up many confusions.
Happy coding!