In this lesson, you will learn why moving a collection in memory can make your existing references to its items dangerous, even if the items themselves haven't changed.
The Heap and the "Moving" Problem
When you create a collection like a Vec<i32>, it stores its data on the heap. Imagine the heap as a massive parking lot. Your Vec acts like a car, and its elements are the passengers inside. When you first create the Vec, it reserves just enough parking spaces for its passengers.
However, if you keep adding elements, the Vec eventually runs out of room. To solve this, it must find a larger "parking spot" elsewhere in memory, move all the passengers to the new location, and tear down the old spot. This process is called reallocation.
Why References Break
If you hold a reference (like &i32) to one of those passengers, your reference is a "map" pointing to a specific address in the old parking spot. After the Vec reallocates, the old address is no longer valid—it might even be occupied by something else entirely!
If your reference continued to point to that old address, you would be looking at "garbage" data or causing memory corruption. This is why Rust’s borrow checker is so strict.
Consider this snippet:
let mut numbers = vec![1, 2, 3];
let first_ref = &numbers[0]; // Pointing to the memory address of '1'
numbers.push(4); // Triggering a potential reallocation
// println!("{}", first_ref); // This would crash the compiler!
When you call push, the Vec might decide to move everything to a new, larger location. Because the Vec is now "mutably borrowed" to add an element, the compiler detects that your existing first_ref could become dangling if a move occurs. Rust prevents this error at compile time, ensuring you never accidentally point to a memory address that the collection has abandoned.
Key Takeaway: Reallocating a collection moves its data to a new memory address, which invalidates any existing references to that data; the borrow checker prevents this by forbidding modifications while references are active.