Python's Flexible Variables
In Python, variables are references to objects, and you can reassign them freely. Python doesn't distinguish between mutable and immutable variables at the language level:
Variable Assignment Comparison
# Python variables are flexible
name = "Alice"
name = "Bob" # Reassignment is always allowed
age = 25
age = 30 # Type can even change
age = "thirty"
// Rust variables are immutable by default
let name = "Alice";
// name = "Bob"; // ❌ This would cause a compile error
let age = 25;
// age = 30; // ❌ This would also fail
Rust's Explicit Mutability
Rust makes mutability explicit. By default, variables are immutable, which helps prevent bugs. If you want to change a variable's value, you must declare it as mutable with the mut
keyword:
Mutable Variables
# Python: All variables are mutable by default
counter = 0
counter += 1 # Works fine
counter = 100 # Also works fine
# Lists are mutable objects
numbers = [1, 2, 3]
numbers.append(4) # Modifies the list
numbers = [5, 6, 7] # Reassigns the variable
// Rust: Explicit mutability
let mut counter = 0;
counter += 1; // ✅ Works because counter is mut
counter = 100; // ✅ Also works
// Vectors need mut to be modified
let mut numbers = vec![1, 2, 3];
numbers.push(4); // ✅ Works because numbers is mut
numbers = vec![5, 6, 7]; // ✅ Reassignment also works
Shadowing: A Rust Superpower
Rust has a unique feature called "shadowing" that allows you to declare a new variable with the same name, effectively hiding the previous one. This is different from reassignment:
Shadowing vs Reassignment
# Python: Variable reassignment
spaces = " "
spaces = len(spaces) # Changes type from str to int
# This is just normal reassignment
# The original string is garbage collected
// Rust: Shadowing allows type changes
let spaces = " ";
let spaces = spaces.len(); // ✅ New variable, different type
// Each 'let' creates a new variable
// The previous one becomes inaccessible
Constants vs Immutable Variables
Both languages have constants, but they work differently:
Constants
# Python constants (by convention)
PI = 3.14159
MAX_USERS = 1000
# These are just variables - Python won't stop you from changing them
PI = 2.71828 # Not recommended, but allowed
// Rust constants (truly immutable)
const PI: f64 = 3.14159;
const MAX_USERS: u32 = 1000;
// Constants must be known at compile time
// const CURRENT_TIME = std::time::SystemTime::now(); // ❌ Won't compile
Practical Example: Counter Implementation
Let's see how you might implement a simple counter in both languages:
Counter Implementation
class Counter:
def __init__(self):
self.value = 0
def increment(self):
self.value += 1
def get_value(self):
return self.value
# Usage
counter = Counter()
counter.increment()
print(counter.get_value()) # Prints: 1
struct Counter {
value: u32,
}
impl Counter {
fn new() -> Self {
Counter { value: 0 }
}
fn increment(&mut self) {
self.value += 1;
}
fn get_value(&self) -> u32 {
self.value
}
}
// Usage
let mut counter = Counter::new();
counter.increment();
println!("{}", counter.get_value()); // Prints: 1
Key Takeaways
- Immutable by default: Rust variables are immutable unless explicitly marked with
mut
- Explicit mutability: This prevents many common bugs and makes code intentions clear
- Shadowing: Allows reusing variable names while potentially changing types
- True constants: Rust constants are compile-time evaluated and truly immutable
- Performance benefits: Immutability enables compiler optimizations
Coming from Python
As a Python developer, you might initially find Rust's approach restrictive. However, this explicitness helps catch bugs at compile time that might only surface during runtime in Python. The compiler becomes your pair programming partner, helping you write more reliable code.
Pro Tip: Start by making variables immutable by default, then add mut
only when you need to modify them. This mirrors Rust's philosophy and will help you write better code.