RustArena

Step 3 of 6

Shadowing

You just saw that writing let again creates a new variable. If the new variable has the same name as an old one, the new one shadows the old: from that line on, the name refers to the new variable.

fn main() {
    let level = 1;
    let level = level + 1;
    let level = level * 10;
    println!("level: {}", level);
}
level: 20

Each line reads the old level on the right and creates a new level on the left. None of them is mutable, and none needs to be: no value was changed; each line made a new variable and the old one can no longer be reached by name.

Shadowing versus mut

They look similar, but there are two real differences:

  1. Shadowing creates a new variable, so it can have a different type (kind of value: number, text, ...). mut only lets you put a new value of the same type into the same variable.
  2. After shadowing, the variable is still immutable, so nothing can change it by accident later.

A typical use is transforming a value step by step while keeping one good name. Here spaces starts as text and becomes a number. .len() gives the length of a piece of text (you'll meet it properly in the strings module):

fn main() {
    let spaces = "    ";
    let spaces = spaces.len();
    println!("{} spaces", spaces);
}
4 spaces

With mut instead, Rust refuses, because a variable's type never changes:

fn main() {
    let mut spaces = "    ";
    spaces = spaces.len();
    println!("{} spaces", spaces);
}
error[E0308]: mismatched types
 --> src/main.rs:3:14
  |
2 |     let mut spaces = "    ";
  |                      ------ expected due to this value
3 |     spaces = spaces.len();
  |              ^^^^^^^^^^^^ expected `&str`, found `usize`

Shadowing inside a block

A pair of braces { } inside a function makes an inner block with its own scope. A variable declared inside it disappears at its closing }, so a shadow made inside a block only lasts until then:

fn main() {
    let x = 5;
    {
        let x = x * 2;
        println!("inside: {}", x);
    }
    println!("outside: {}", x);
}
inside: 10
outside: 5

Common mistakes

Your task

A run is recorded in kilometers. Use shadowing (a second let, no mut) to convert distance from kilometers to meters, so the program prints 5000 m.

Challenge 2

Fill in the keyword twice so price is shadowed once in main and once inside the inner block. The program should print inside: 100 and then outside: 50.

Challenge 3

Two concert tickets cost 120 each, and there is a coupon for 20 off each ticket. Starting from let price = 120;, shadow price twice, first applying the coupon and then multiplying by the two tickets, and print Total: 200. Use only let (no mut), and don't type 200 yourself.

Previous: Immutable by default, and mut Next: Types: annotations and inference