Sitemap

Rust Quick Notes: Shadowing, Mutability & Memory

3 min readSep 10, 2025

--

Rust is a systems programming language that emphasizes safety and performance. Its unique approach to variables, ownership, and memory management can feel tricky at first. Here’s a concise guide to understanding shadowing, mutability, and memory in Rust, along with practical examples.

Get Zain Subhani’s stories in your inbox

Join Medium for free to get updates from this writer.

In Rust, variables are immutable by default:

let x = 10;
  • Once assigned, x cannot be changed.
  • Immutability is safe for concurrency — multiple threads can read an immutable variable without requiring locks.
  • Pro Tip: Start with immutability and only make variables mutable when necessary.

2️⃣ Mutability

To allow a variable to change, you must explicitly declare it as mutable:

let mut x = 10;
x = 20; // ✅ valid

The mut keyword only applies to the current binding.

  • Shadowed versions do not inherit mutability.

3️⃣ Shadowing

Shadowing allows you to redeclare a variable with the same name:

let x = "123";
let x = x.parse::<i32>().unwrap(); // shadowed x, now an integer
let x = x + 1; // x is now 124
  • You can change the type when shadowing.
  • The original binding is hidden, not modified.
  • Shadowing works even for immutable variables.

4️⃣ Copy vs Move

Rust differentiates between Copy and non-Copy types:

// Copy type
let a = 5;
let b = a; // a is still valid

// Non-Copy type
let s1 = String::from("Rust");
let s2 = s1; // ❌ s1 is now invalid
let s3 = s2.clone(); // ✅ s2 and s3 are valid, separate heap memory
  • Copy types (e.g., i32, bool) are duplicated on assignment.
  • Non-Copy types (e.g., String, Vec) are moved unless explicitly cloned.
  • Use .clone() to create a separate heap copy.

5️⃣ Shadowing + Mutability

Shadowing interacts interestingly with mutability:

let mut x = 5;
let x = x + 2; // shadowed x is now immutable
// x = 10; // ❌ error
  • Shadowing an immutable variable from a mutable one creates a new immutable binding.
  • Mutability does not carry over to shadowed variables.

6️⃣ Memory Model

Understanding Rust’s memory model is key:

  • Shadowed variables occupy new stack slots.
  • Types like String and Vec store data on the heap, while the stack holds a pointer, length, and capacity.
  • Moves transfer ownership, preventing double free errors at compile-time.
  • Cloning allocates new heap memory, giving two independent owners.

7️⃣ Concurrency

Rust ensures thread safety through ownership:

  • Immutable variables are safe to share across threads.
  • Mutable variables require locks (Mutex, RwLock) or exclusive access.
  • Shadowing does not affect thread safety.
// Shadowing allows type change
let x = "123";
let x = x.parse::<i32>().unwrap();
let x = x + 1; // x is now 124

// Move vs clone
let s1 = String::from("Rust");
let s2 = s1; // s1 invalid
let s3 = s2.clone(); // s2 and s3 valid, separate heap memory

// Mutability + shadowing
let mut x = 5;
let x = x + 2; // immutable shadowed x
// x = 10; // ❌ error

Conclusion

Rust’s rules around shadowing, mutability, and memory may seem strict, but they are what make it safe and concurrent-friendly by design. Start by writing immutable code, leverage shadowing for transformations, and understand when ownership moves or clones occur. Once you internalize these patterns, managing memory and concurrency in Rust becomes much easier.

--

--