Skip to main content

Variables & Mutability

If you have ever used another programming language, the concept of variables should be familiar.

// A couple Rust variables.
// The first holds a name (a slice of text), and the other, an age (a number).
let my_name: &str = "Bader";
let age: u32 = 22;
// We'll dive more into this line later, but just know that it is a way to print information.
println!("My name is {my_name} and I am {age} years old.");

As in other languages, variables in Rust behave mostly the same with the exception of a few unique properties, the most prominent being mutability and shadowing .

info

All variables in Rust are immutable by default. Immutable variables cannot be changed unless explicitly declared as mutable. This prevents unwanted changes to values in code.

Immutability in Rust

As mentioned before, by default, all variables in Rust are immutable. This means that once a variable has been declared, the value within cannot be changed.

// The compiler won't allow this to be changed.
// This code would throw an error!

let age: u32 = 22;
// Increment the age by one.
age += 1;

======

error[E0384]: cannot assign twice to immutable variable `age`
--> src/main.rs:26:1
|
24 | let age: u32 = 22;
| ---
| |
| first assignment to `age`
| help: consider making this binding mutable: `mut age`
25 | // Increment the age by one.
26 | age += 1;
| ^^^^^^^^ cannot assign twice to an immutable variable

This is yet another example of how the Rust compiler prevents any illegal operations, along with a descriptive way of letting the developer know. The most interesting line to note is cannot assign twice to immutable variable, as this clearly shows the immutability property of the variable.

This same error is actually incredibly useful, as in some cases, we sometimes want to keep the value of a variable the same. The Rust compiler, by default, keeps this safe by keeping it immutable. There are many practical reasons for this, i.e., multi-developer projects, where one developer can see the program's intentions by whether a variable is mutable.

A variable's value changing unintentionally can also cause trivial bugs, but luckily the Rust compiler deals with it elegantly using immutability.

Of course, variables aren't always meant to be immutable. To allow a variable to become mutable, use the mut keyword before the variable name when declaring it:

// This works now!  Notice the `mut`, short for mutable, after `let`
let mut age: u32 = 22;
// Increment the age by one.
age += 1;

Shadowing in Rust

Along with variability, Rust introduces the concept of shadowing. Take a look at this example:

let x = 10u32;
let x: &str = "Hello!";

// Prints "Hello!"
println!("{x}");

At first glance, this may seem odd - why are there two variables with the same name and different type? Shouldn't Rust's type system prevent this from occurring?

This is what's called shadowing. The Rust compiler will take the latest value assigned to that variable name, in this case from the number 10 to Hello!, and utilize that until either the scope ends or it is shadowed again.

Shadowing is not the same as declaring a variable mutable with mut, as it remains immutable after being shadowed. The let keyword must also be used to shadow a variable, take a look at the following example:


let x = 10u32;
x = "Hello!";

The above code will fail to compile, as no new assignment is being made via let. It is trying, in essence, to assign a number of type u32 to a slice of text.


Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/main.rs:3:5
|
2 | let x = 10u32;
| ----- expected due to this value
3 | x = "Hello!";
| ^^^^^^^^ expected `u32`, found `&str`

Shadowing with let reassigns the variable to a new type and value, redefining it altogether.

Constants in Rust

As with traditional, immutable variables in Rust, constants are also immutable - permanently. Constants in Rust cannot be made into mutable variables with mut. Another difference is that constants only may be set to a constant expression, meaning the value is hardcoded and not calculated as the result of some function in runtime.


const THREE_HOURS_IN_SECONDS: u32 = 60 * 60 * 3;

The naming convention for constants is all uppercase, with underscores between each word. They can be used for declaring some standard, global, and constant variables within your Rust program.

Try it out!

Try out some of these concepts yourself! There are a few things that may seem unfamiliar here, which will be covered on the next page, however - have a look at the code and familiarize yourself:

  • u32 means that a variable is a number.
  • &str means that the variable is a string literal.

What is happening here?

Three primary concepts are being put to use here - namely, immutability and shadowing. Initially, we declare two variables - name and age. age is mutable, as age is expected to change, however, a name is meant to be immutable. However, age is set to a type that we can't add to easily.

This is where shadowing comes into play. Using shadowing, redefine age to be a number of type u32 instead. This allows us to add to the age easily.