Beginner · Getting started with Rust · topic 4 of 50 · 25 min
Variables, mutability, shadowing and constants
Explanation
Why are variables fixed by default?
A variable is a name for a value. In most languages, any line can change any variable. In a large program that causes bugs: a value changes somewhere far away and you cannot find where. Rust makes variables immutable by default. Immutable means "cannot be changed". If you want a variable to change, you must say so with mut. Then anyone reading the code knows exactly which values move.
fn main() {
let price = 100;
price = 120; // error[E0384]: cannot assign twice to immutable variable
}fn main() {
let mut price = 100;
println!("Old price: {price}");
price = 120;
println!("New price: {price}");
}Types are fixed, even with mut
mut lets the value change, but not the type. A type is the kind of value: a whole number, a decimal number, text and so on. Rust works out the type from the first value; this is called type inference. You can also write the type yourself after a colon.
let mut count: i32 = 5;
count = 6; // fine: still a number
// count = "six"; // error: expected i32, found &strShadowing
Shadowing means writing let again with the same name. This creates a brand-new variable that hides the old one. It is useful when you transform a value step by step, and it can even change the type.
fn main() {
let input = " 42 ";
let input = input.trim(); // still text, without spaces
let input: i32 = input.parse().unwrap(); // now a number
println!("{}", input + 1);
}| let mut x | let x again (shadowing) | |
|---|---|---|
| Creates a new variable? | No, changes the same one | Yes |
| Can change the type? | No | Yes |
| Result is mutable? | Yes | Only if you write let mut |
Shadowing also works inside a block (code in curly braces). The inner variable exists only until the closing brace; after it, the outer one is visible again.
let x = 5;
{
let x = x * 2;
println!("inside: {x}"); // inside: 10
}
println!("outside: {x}"); // outside: 5Constants
A constant is a value fixed for the whole program, like a GST rate or the number of seconds in a day. Write it with const, an UPPER_CASE name, and the type is required. Its value must be known when the program is compiled, so it cannot come from user input.
const GST_PERCENT: u32 = 18;
const SECONDS_PER_DAY: u32 = 24 * 60 * 60;
fn main() {
let price = 500;
println!("GST: {}", price * GST_PERCENT / 100);
}There is also static, a value with one fixed place in memory for the whole run. You will rarely need it as a beginner; prefer const.
Names and warnings
- Variables and functions use snake_case: total_price, not totalPrice.
- Constants use SCREAMING_SNAKE_CASE: MAX_USERS.
- An unused variable gives a warning. Start its name with an underscore (_temp) if it is unused on purpose.
Common mistakes
- Adding mut everywhere "just in case". The compiler warns "variable does not need to be mutable". Remove mut when the value never changes.
- Trying to change the type of a mut variable. Use shadowing with a new let instead.
- Writing const without a type, like const RATE = 18;. Add the type: const RATE: u32 = 18;.
- Confusing shadowing with changing: after let x = x + 1; inside a block, the outer x is unchanged.
Need help?
Stuck on a word or an idea? Search this course, or every course, and jump straight to the explanation or a worked question.
Try:
Watch the lesson
Variables, mutability, shadowing and constants · 1/7
Variables in Rust have one surprise: they cannot change unless you ask.
Prefer a video by a teacher? Find free videos on “rust variables mutability shadowing constants explained” on YouTube ↗
In real life: Cleaning a mobile number from a form
A signup form for a food delivery app gets a mobile number as text with spaces and a +91 prefix. The code shadows the same name step by step: trim the spaces, remove the prefix, then check that 10 digits remain. Each step makes a new, immutable variable, so no later line can change the value by accident.
fn main() {
let phone = " +91 9876543210 ";
let phone = phone.trim();
let phone = phone.trim_start_matches("+91").trim();
println!("{phone} has {} digits", phone.len());
}Practice (0/3 done)
What does this print? fn main() { let x = 3; let x = x * 2; { let x = x + 1; println!("{x}"); } println!("{x}"); }
Which change makes this compile? fn main() { let total = 0; total = total + 50; }
Write a program for a mobile recharge: a constant for GST (18%), a mut variable for the wallet balance that starts at 500, then subtract a recharge of 199 plus GST and print the new balance. Then use shadowing to turn the text " 2 " into the number 2 and print it doubled.
Quick quiz
Read more (free)
Related help
Before this, learn
This topic builds on these. If something here feels new, open them first.
Related topics
- Variables, types and constantsC++ Programming (modern C++ and DSA) · First steps: compiling, output, variables and input
- Variables: let, const and varJavaScript Programming (language in depth) · First steps: values, variables and operators
- Types and variablesC# and .NET · Getting started with C# and .NET
- Shadowing and thinking in EnglishSpoken English: Zero to Fluent · Fluency and confidence
- Constants, iota and type conversionsGo Programming (backend and cloud) · Getting started with Go
- static members and finalJava Programming (Core to Spring Boot) · Classes and objects
Questions people ask about this
Open a question to see the answer and why. The first ones are this topic's own quiz, so try the quiz above before you peek.
By default, a Rust variable created with let is…
Answer: Immutable
Variables are immutable unless you write let mut.
Which can change a variable's type?
Answer: Shadowing with a new let
Shadowing creates a new variable, so the new one can have a different type.
Which constant is written correctly?
Answer: const RATE: u32 = 18;
A const needs a type and a value; the name is UPPER_CASE by convention.
What does the compiler say if you mark a variable mut but never change it?
Answer: A warning: variable does not need to be mutable
It is a warning, not an error. Remove mut to keep code clear.
Which name follows Rust style for a variable?
Answer: total_price
Variables and functions use snake_case. UPPER_CASE is for constants.
What is in an int variable declared inside main with int n; and no value?Related
Answer: Garbage: reading it is undefined behaviour
Local variables are not set to zero automatically. Always initialise them.
Which keyword should you use by default for new variables?Related
Answer: const
Use const by default and switch to let only when you must reassign.
const int Max = 10; Max = 20; What happens?Related
Answer: Compile error: a const cannot be changed
A const value is fixed at compile time and cannot be assigned again.
From Types and variables
What is the first step of shadowing?Related
Answer: Listen once to understand the meaning
Understand the meaning first; then read along, then shadow.
Pass the quiz (60% or more) or mark this topic as done when you are ready.
Your progress is saved on this device only.
Next steps
- This is the last topic of “Getting started with Rust”. The module exam comes next.
- Module 1 exam: Getting started with Rust · 12 questions · 20 min, without help. 4 topics of this module still to finish first.
- Then the next module, Types, control flow and functions. 50 topics left in the course before the final exam.