Let Statements

A let statement introduces a new variable binding.

let_stmt = "let" [ "mut" ] IDENT [ ":" type ] "=" expression ";" ;

Immutable Bindings

By default, variables are immutable. An immutable variable MUST NOT be reassigned.

fn main() -> i32 {
    let x = 42;
    x
}

Mutable Bindings

The mut keyword creates a mutable binding that MAY be reassigned.

fn main() -> i32 {
    let mut x = 10;
    x = 20;
    x
}

Type Annotations

Type annotations are optional when the type can be inferred from the initializer.

When a type annotation is present, the initializer MUST be compatible with that type.

fn main() -> i32 {
    let x: i32 = 42;      // explicit type
    let y = 10;           // type inferred as i32
    let z: i64 = 100;     // 100 inferred as i64
    x + y
}

Shadowing

A variable MAY shadow a previous variable of the same name in the same scope.

When shadowing, the new variable MAY have a different type.

The scope of a binding introduced by a let statement begins after the complete let statement, including its initializer. The initializer expression is evaluated before the new binding is introduced, so references to a shadowed name within the initializer resolve to the previous binding.

fn main() -> i32 {
    let x = 10;
    let x = x + 5;  // shadows previous x, initializer uses old x
    x  // 15
}