In Rust, variables are fundamental for storing data. A key concept to grasp is that variables are immutable by default. This means once you bind a value to a variable name, you cannot change that value later. This design choice by Rust promotes safer code, makes it easier to reason about data flow, and helps prevent bugs, especially in concurrent programming contexts.
To declare a variable, you typically use the `let` keyword:
`let x = 5;`
Here, `x` is an immutable variable. If you try to reassign `x` later:
`x = 6;`
Rust will throw a compile-time error, informing you that `x` is immutable.
Making Variables Mutable
If you need to change the value bound to a variable, you must explicitly declare it as mutable using the `mut` keyword:
`let mut y = 10;`
Now, `y` can be reassigned:
`y = 20;`
This ability to change values is often necessary for tasks like counters, state management, or modifying elements within a collection.
Why Immutability by Default?
1. Safety: Prevents accidental changes to data, reducing a common source of bugs.
2. Predictability: When you see a variable, you know its value won't change unless explicitly marked `mut`. This makes code easier to read and understand.
3. Concurrency: Immutable data is inherently thread-safe. When data can't change, multiple threads can read it simultaneously without needing complex synchronization mechanisms, which simplifies concurrent programming.
Shadowing
Rust also allows for a concept called shadowing, which is distinct from mutability. Shadowing lets you declare a *new* variable with the *same name* as a previous variable. The new variable "shadows" the old one, meaning the old variable is no longer accessible after the new one is declared.
`let z = 5; // z is 5`
`let z = z + 1; // z is now 6 (a new z)`
`let z = "hello"; // z is now a string (a new z with a different type)`
Key differences between `mut` and shadowing:
* Mutability (`mut`): Modifies the value of an *existing* variable. The variable's type cannot change.
* Shadowing (`let`): Creates a *new* variable, hiding the old one. The new variable can have a different type than the old one. Shadowing is useful when you want to transform a value but keep using the same variable name.
Constants
Finally, Rust has constants, declared using the `const` keyword:
`const MAX_POINTS: u32 = 100_000;`
Constants are always immutable, cannot be declared `mut`, and must have their type explicitly annotated. Unlike variables, constants can only be set to a constant expression, not the result of a function call or any value that could only be computed at runtime.
Example Code
fn main() {
// 1. Immutable variable (default behavior)
let x = 5;
println!("The value of immutable x is: {}", x);
// Attempting to reassign an immutable variable will result in a compile-time error:
// x = 6; // Uncommenting this line will cause an error:
// // error[E0384]: cannot assign twice to immutable variable `x`
// 2. Mutable variable
let mut y = 10;
println!("The value of mutable y initially is: {}", y);
y = 20; // This is allowed because y is mutable
println!("The value of mutable y after change is: {}", y);
// 3. Shadowing
let z = 100; // Original z
println!("The value of z (original) is: {}", z);
let z = z + 1; // Shadow z with a new value
println!("The value of z (shadowed numeric) is: {}", z);
let z = "a string value"; // Shadow z again, this time with a different type
println!("The value of z (shadowed string) is: {}", z);
// Demonstrate that the original z (numeric) is no longer accessible
// println!("Original z after string shadowing: {}", z_original); // This would not compile
// 4. Constants (always immutable and require type annotation)
const APPLICATION_VERSION: &str = "1.0.0";
const MAX_BUFFER_SIZE: usize = 1024;
println!("Application Version: {}", APPLICATION_VERSION);
println!("Max Buffer Size: {}", MAX_BUFFER_SIZE);
// Attempting to change a constant will also be a compile-time error
// APPLICATION_VERSION = "1.1.0"; // error[E0070]: invalid left-hand side of assignment
}








Variables and Mutability