Rust is statically typed: meaning that every variable has a known type at compile time.
They can be explicitly declared as shown in Primitives, but they can also be inferred.
Mutability
Variables are immutable by default, but can be overridden using the mut modifier.
fn main() {
let _immutable_binding = 1;
let mut mutable_binding = 1;
mutable_binding += 1; // ok
_immutable_binding += 1; // not ok
}Scope and Shadowing
Variable bindings are constrained to live inside a block (enclosed by {}), the moment you leave the block, the variable unbinds.
fn main() {
let shadowed_binding = 1;
{
println!("before being shadowed: {}", shadowed_binding); // 1
// This binding *shadows* the outer one
let shadowed_binding = "abc";
println!("shadowed in inner block: {}", shadowed_binding); /// abc
}
println!("outside inner block: {}", shadowed_binding); // 1
// This binding *shadows* the previous binding
let shadowed_binding = 2;
println!("shadowed in outer block: {}", shadowed_binding); // 2
}Declare First
You can declare a variable first and initialize it later. But MUST be initialized before being used, else its undefined behaviour.
fn main() {
// Declare a variable binding
let a_binding;
{
let x = 2;
// Initialize the binding
a_binding = x * x;
}
println!("a binding: {}", a_binding);
let another_binding;
// Error! Use of uninitialized binding
println!("another binding: {}", another_binding);
// FIXME ^ Comment out this line
another_binding = 1;
println!("another binding: {}", another_binding);
}Freezing
This is referring to the process of shadowing a mutable variable as immutable within a scope
fn main() {
let mut mutable = 22i32;
{
let mutable = mutable; // freezes it, its no longer mutable!
mutable += 1; // error!!
}
mutable += 1; // allowed again since we arte outside of scope
}