Skip to main content
eLearner.app
Module 1 · Lesson 1 of 21/14 in the course~10 min
Module lessons (1/2)

Variables and Mutability

In Rust, variable and memory management is guided by two fundamental principles: safety and control. For this reason, unlike many other programming languages, variables in Rust are immutable by default.

This means that once a value is assigned to a variable, it cannot be changed. If you attempt to modify it, the compiler will raise an error.

Immutable Variables and the mut Keyword

To declare a variable, we use the let keyword:

Code
let x = 5; // Immutable by default
// x = 6; // ERROR! The compiler will refuse to build this code.

If you need to modify the value of a variable, you must explicitly make it mutable by using the mut keyword right after let:

Code
let mut y = 10; // Mutable
println!("Il valore di y e: {}", y);
y = 15; // Valid!
println!("Il valore modificato di y e: {}", y);

Using the mut prefix clearly communicates to the compiler and to anyone reading the code that the value will change during execution.

Shadowing

Rust also allows the concept of shadowing, which is the re-declaration of a variable with the same name using the let keyword again. The new variable "shadows" (or covers) the previous one:

Code
let x = 5;
let x = x + 1; // Shadowing: x is now 6
let x = x * 2; // Shadowing: x is now 12

Unlike mut, shadowing allows you to:

  1. Change the data type of a variable while keeping the same name.
  2. Maintain the immutability of the variable after the transformations.
Code
let spaces = "   "; // Type: &str (string)
let spaces = spaces.len(); // Type: usize (number)

Usefulness of Shadowing and Scopes

Shadowing is not limited to the same block; it can be used within nested blocks ({}) to temporarily override a value. When the inner block ends, the original variable becomes visible again:

Code
let x = 5;
{
    let x = x * 2; // Shadowing valid only inside the block
    println!("Value in the inner block: {}", x); // Prints 10
}
println!("Value in the outer block: {}", x); // Prints 5

This mechanism is extremely safe and efficient because it occurs entirely at compile-time, ensuring type safety without any runtime performance overhead.


Try it yourself

Exercise#rust.m1.l1.e1
Attempts: 0Loading…

Declare a mutable variable named x with an initial value of 5. Next, add 10 to x and print it to the screen using the println! macro.

Loading editor…
Show hint

Declare the variable with `let mut x = 5;`, then increment it with `x += 10;` (or `x = x + 10;`) and finally print it with `println!('{}', x);`.

Solution available after 3 attempts

Exercise#rust.m1.l1.e2
Attempts: 0Loading…

Declare an immutable variable y with a value of 10. Shadow y by declaring it again with let to multiply its previous value by 2, and finally print it using println!.

Loading editor…
Show hint

Use `let y = 10;`, then shadow it by declaring it again with `let y = y * 2;` and finally use `println!` to display it.

Solution available after 3 attempts

Exercise#rust.m1.l1.e3
Attempts: 0Loading…

Declare an immutable variable named input containing the string value "42". Shadow input by declaring it as an integer of type i32, converting the original value via input.parse::<i32>().unwrap(). Finally, print it using the println! macro.

Loading editor…
Show hint

Declare `let input = "42";`, then shadow it with `let input: i32 = input.parse().unwrap();` and print it using `println!("{}", input);`.

Solution available after 3 attempts