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
}
Struct Destructuring
A let statement may destructure a struct value, binding each field to a new variable. The struct type name must be specified, and all fields must be listed.
let_destructure = "let" type_name "{" field_bindings "}" "=" expression ";" ;
field_bindings = field_binding { "," field_binding } [ "," ] ;
field_binding = [ "mut" ] IDENT [ ":" ( IDENT | "_" ) ] ;
The expression must evaluate to the named struct type. The struct value is consumed by the destructuring — it is no longer accessible after destructuring.
All fields of the struct MUST be listed in the destructuring pattern. Omitting a field is a compile-time error.
A field binding of the form field (shorthand) binds the field value to a new variable with the same name. A binding of the form field: name binds the field value to a variable named name. A binding of the form field: _ discards the field value, dropping it immediately if the type has a destructor.
struct Point { x: i32, y: i32 }
fn main() -> i32 {
let p = Point { x: 10, y: 32 };
let Point { x, y } = p; // p is consumed
x + y // 42
}
struct Pair { a: i32, b: i32 }
fn main() -> i32 {
let p = Pair { a: 1, b: 2 };
let Pair { a: first, b: _ } = p; // rename a to first, discard b
first // 1
}