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:
- Shadowing creates a new variable, so it can have a different type (kind of value: number, text, ...).
mutonly lets you put a new value of the same type into the same variable. - 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
- Expecting a shadow inside a block to change the outer variable. It doesn't: it's a separate variable that disappears at the
}. To change the outer one, make itmutand assign withoutlet. - Using shadowing as a counter. Shadowing is idiomatic for transforming a value (text into a number, a raw value into a cleaned one). For something that keeps changing, like a score or a balance, use
mut.
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