Skip to content
AgileLogics
6 min read

Variables, Mutability & Constants

Prerequisites: How Rust code runs

Syntax

let x = 5;               // immutable: can't be changed
let mut count = 0;       // mutable: can be changed
let price: f64 = 9.99;   // with an explicit type
const MAX_USERS: u32 = 100;

Variables are declared with let and are immutable by default — once a value is bound to the name, it can't change. Add mut when you need to change it. Defaulting to immutable makes code easier to reason about: if a variable isn't mut, you know its value never changes.

The compiler usually infers the type from the value. You can also write it explicitly after a colon.

Shadowing means declaring a new variable with the same name using let again. The new variable replaces the old one from that point on, and it can even have a different type:

let input = "42";
let input: i32 = 42; // a new `input`, now a number

Constants (const) are always immutable, must have a type annotation, and must be set to a value known at compile time. By convention their names are SCREAMING_SNAKE_CASE. Constants can be declared outside any function, making them available everywhere in the file.

Inside a println! string, {name} prints the variable name — the Printing & Formatting lesson covers this in detail.

Example

const MAX_LEVEL: u32 = 10;

fn main() {
    let name = "Ada";
    println!("Player: {name}");

    let mut level = 1;
    println!("Level: {level}");
    level += 1;
    level += 1;
    println!("Level: {level} of {MAX_LEVEL}");

    let spaces = "   ";
    let spaces = spaces.len(); // shadowing: now a number
    println!("Spaces: {spaces}");

    let x = 5;
    {
        let x = x * 2; // shadows `x` only inside this block
        println!("Inner x: {x}");
    }
    println!("Outer x: {x}");
}

Expected output

Player: Ada
Level: 1
Level: 3 of 10
Spaces: 3
Inner x: 10
Outer x: 5

Common mistakes

  • Changing a variable that isn't mut:
let x = 5;
x = 6;
error[E0384]: cannot assign twice to immutable variable `x`

The compiler's help line suggests the fix: let mut x = 5;.

  • Leaving the type off a constant. const MAX = 10; fails with:
error: missing type for `const` item
  • Confusing shadowing with mutation. let x = x + 1; creates a new variable; x = x + 1; changes an existing mut variable. Shadowing can change the type; mutation can't.
  • Ignoring "unused variable" warnings. They often point to a typo or logic mistake. Prefix a name with _ (like _temp) if it's unused on purpose.

Practice

Declare a constant SECONDS_PER_MINUTE set to 60. Then create a mutable variable total_seconds starting at 0, add 3 minutes' worth of seconds to it, then 30 more seconds, and print Total: 210 seconds.

Show solution
const SECONDS_PER_MINUTE: u32 = 60;

fn main() {
    let mut total_seconds = 0;
    total_seconds += 3 * SECONDS_PER_MINUTE;
    total_seconds += 30;
    println!("Total: {total_seconds} seconds");
}