Lesson 1
Variables, mutability and types
Tell mut apart from shadowing, recognize Rust's scalar types, and group values with tuples and arrays.
Same name, different rules
In “Variables, functions and control flow” you saw let and mut through a
simple metaphor, without naming shadowing, sized scalar types, or tuples and
arrays. Here we pick up the same pieces with the vocabulary and rigor you will
use for the rest of the course: which type Rust picks by default, when you
have to annotate it yourself, and what actually differs between mutating a
value and rebinding a name.
Everything that follows — from structs to iterators — assumes you can tell a
let mut apart from shadowing, and that you recognize Rust’s four scalar
types. This lesson builds that foundation with code that actually compiles
and runs, not just pseudocode.
You will go through five stations: mutability, shadowing, scalar types, fixed-size compound types, and inference. Each one ends with a real example you can run or edit.
What changes compared with other languages
| Language | Default declaration | How you declare it can change |
|---|---|---|
| JavaScript | let is mutable by default |
Nothing extra needed; const does block reassignment |
| Python | Every variable is reassignable | There is no keyword to mark a variable as immutable |
| Rust | let is immutable by default |
You must write mut explicitly |
The difference is not just syntactic: in Rust, “immutable by default” means the compiler rejects the reassignment before running anything, not that there is a convention you could choose to ignore.
Exercise 01 · Distance converter
Split a distance into whole kilometers and leftover meters.
The starter already declares the signature with two integers grouped in a tuple return. Complete the body using integer division and the modulo operator.
Objective
Complete split_kilometers so it returns (whole_kilometers, remaining_meters) from a total distance in meters.
Implement
Replace `todo!` with the division and remainder of `total_meters` by 1000.
Function 01
Split kilometers and meters
Calculate whole kilometers with `/` and the remaining meters with `%`, and return them as a tuple.
/// Splits a total distance in meters into whole kilometers and the remaining meters.
pub fn split_kilometers(total_meters: u32) -> (u32, u32) {
todo!("return (total_meters / 1000, total_meters % 1000)")
}Check
Run three visible cases and use the diagnostic to correct the code.
Visible tests
Modify the code before running the tests.
Help
View progressive hints or compare with the solution.
/// Splits a total distance in meters into whole kilometers and the remaining meters.
#[must_use]
pub fn split_kilometers(total_meters: u32) -> (u32, u32) {
let kilometers = total_meters / 1000;
let remaining_meters = total_meters % 1000;
(kilometers, remaining_meters)
}
// Playground entry point for this example.
fn main() {
let (km, m) = split_kilometers(1500);
println!("{km} {m}");
}Summary
letcreates variables that are immutable by default;let mutexplicitly declares that a variable will change.- Shadowing rebinds a name with a new
letand can change type;mutchanges the value without being able to change its type. - Rust has four scalar types: integers (
i32by default), floats (f64by default),bool, andchar. - Integer overflow panics in debug builds and wraps around in release builds.
- Tuples group different types and arrays group the same type; both have a fixed size.
- The compiler infers types almost all the time; when it cannot decide, as with
parse, it requires an explicit annotation.