Phase 1Getting started

#3 Variables and types

let, mut, const, types

Immutable by default

In Rust, variables are immutable by default. Once you bind a value to a name, you cannot change it. This is one of Rust's key safety features — it helps prevent accidental modifications.

Immutable variable
let x = 5;
println!("x = {x}");

// x = 6;  // ERROR: cannot assign twice to immutable variable

The let keyword declares a variable. If you try to reassign it, the compiler will stop you with a clear error message.

Mutable variables (mut)

When you need a variable that can change, add the mut keyword:

Mutable variable
let mut x = 5;
println!("x = {x}");

x = 6;
println!("x = {x}");

The mut keyword signals your intent clearly: this value will change. It makes your code easier to reason about because mutability is always explicit.

Constants

Constants are values that are always immutable. Unlike let variables, constants must have their type annotated and can be declared in any scope, including global scope:

Constants
const MAX_POINTS: u32 = 100_000;
println!("MAX_POINTS = {MAX_POINTS}");

// Constants must be known at compile time
// They cannot use `mut` and must have a type annotation

By convention, constants use SCREAMING_SNAKE_CASE. The underscores in numbers (100_000) are visual separators that make large numbers easier to read.

Basic types

Rust is statically typed — every value has a type known at compile time. Here are the fundamental types:

Integer types
let a: i32 = 42;      // Signed 32-bit integer (default)
let b: u8 = 255;      // Unsigned 8-bit integer
let c: i64 = -1000;   // Signed 64-bit integer
let d: usize = 10;    // Pointer-sized unsigned integer
Other basic types
let pi: f64 = 3.14;           // 64-bit float (default)
let active: bool = true;       // Boolean
let grade: char = 'A';         // Unicode character
let hello: &str = "Hello, Rust!"; // String slice

Rust can often infer the type from context, so you don't always need to write the annotation. But being explicit helps readability, especially for beginners.

Shadowing

Rust allows you to declare a new variable with the same name as a previous one. The new variable shadows the old one:

Shadowing
let x = 5;
let x = x + 1;    // x is now 6
let x = x * 2;    // x is now 12

// You can even change the type!
let spaces = "   ";       // &str
let spaces = spaces.len(); // usize

Shadowing is different from mut: you create a brand new variable each time. This lets you reuse names and even change types.

Your turn

Try running the variables demo with cargo run and cargo check in the terminal below:

terminal — cargo
user@stemlegacy:~/variables-demo$