Lesson 1

Variables, mutability and types

Tell mut apart from shadowing, recognize Rust's scalar types, and group values with tuples and arrays.

Level
Beginner
Duration
22 min
Updated

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.

Variable state

  • STATE · attemptsinitial value: 3
  • RULE · let mutthe change is declared
  • RESULT · attempts updatedwithout mut, this would not compile
01 / 05LET · IMMUTABLE BY DEFAULT

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.

0/1correct

Complete split_kilometers so it returns (whole_kilometers, remaining_meters) from a total distance in meters.

01

Implement

Replace `todo!` with the division and remainder of `total_meters` by 1000.

Function 01

Split kilometers and meters

Unchanged

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)")
}
02

Check

Run three visible cases and use the diagnostic to correct the code.

divides correctly for both exact and non-exact distances
Compiler

Modify the code before running the tests.

03

Help

View progressive hints or compare with the solution.

Run an attempt to unlock the solution.

Summary

  • let creates variables that are immutable by default; let mut explicitly declares that a variable will change.
  • Shadowing rebinds a name with a new let and can change type; mut changes the value without being able to change its type.
  • Rust has four scalar types: integers (i32 by default), floats (f64 by default), bool, and char.
  • 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.

Sources