Variables and Mutability

Variables and Mutability

Learn about variables, mutability, constants, and shadowing in Rust.

15 min read•4 learning objectives

What You'll Learn

  • Understand immutability by default in Rust
  • Learn how to create mutable variables
  • Differentiate between variables and constants
  • Master variable shadowing

Variables and Mutability

In Rust, variables are immutable by default. This is one of Rust's key features that helps you write safer, more predictable code.

Declaring Variables

Use the let keyword to declare variables:

Rust
fn main() {
    let x = 5;
    println!("The value of x is: {}", x);
    
    // This would cause a compile error:
    // x = 6;  // ❌ Cannot assign twice to immutable variable
}

The code above creates an immutable variable x with the value 5. If you try to change it, Rust will give you a compile-time error.

Mutable Variables

To make a variable mutable, use the mut keyword:

Rust
fn main() {
    let mut x = 5;
    println!("The value of x is: {}", x);
    
    x = 6;  // ✅ This works!
    println!("The value of x is: {}", x);
}

Constants

Constants are similar to immutable variables, but with key differences:

Rust
const MAX_POINTS: u32 = 100_000;

fn main() {
    println!("Maximum points: {}", MAX_POINTS);
}

Constants vs Variables:

  • Constants use const instead of let
  • You must annotate the type (e.g., u32)
  • Constants can be declared in any scope, including global
  • Constants must be set to a constant expression, not computed at runtime
  • Naming convention: SCREAMING_SNAKE_CASE

Shadowing

Rust allows you to declare a new variable with the same name as a previous variable. This is called shadowing:

Rust
fn main() {
    let x = 5;
    let x = x + 1;  // Shadows the previous x
    
    {
        let x = x * 2;  // Shadows in inner scope
        println!("Inner scope x: {}", x);  // 12
    }
    
    println!("Outer scope x: {}", x);  // 6
}

Shadowing vs Mutability:

Shadowing is different from marking a variable as mut. With shadowing, you're creating a new variable, which means you can even change its type:

Rust
fn main() {
    let spaces = "   ";           // String type
    let spaces = spaces.len();    // Number type - this works!
    
    // With mut, this would NOT work:
    // let mut spaces = "   ";
    // spaces = spaces.len();  // ❌ Type mismatch error
}

Practice Exercise

Try to predict what this code will output:

Rust
fn main() {
    let x = 10;
    let mut y = 20;
    
    y = y + x;
    let x = x * 2;
    
    println!("x = {}, y = {}", x, y);
}