Borrowing and Collections
Collections such as Vec<T>, String, HashMap<K, V>, and VecDeque<T> store their elements in a single dynamically resizable heap buffer. Because resizing, inserting, or removing elements can reallocate or shift the entire heap buffer, borrowing even a single element (&vec[0] or map.get("key")) borrows the entire collection for the lifetime of that element reference.
Why Borrowing One Element Locks the Whole Collection
Unlike struct fields (s.a and s.b), which have fixed compile-time offsets on the stack, a collection's indexing operator vec[i] is a method call (Index::index(&self, i) -> &T). Because its signature takes &self, holding &vec[0] keeps the entire vec immutably borrowed:
let first: &i32 = &vec[0]; (Calls `Index::index(&vec, 0)` -> borrows ALL of `vec` as `&Vec<i32>`)
vec.push(99); (Requires `&mut vec` -> ❌ E0502 while `first` is still in use!)
fn main() {
let mut scores = vec![80, 90, 100];
// 1. Copy the `i32` value out (or finish using the reference) BEFORE mutating the Vec:
let first_val: i32 = scores[0]; // Copies the `i32`; borrow of `scores` ends on this line!
scores.push(110); // Allowed!
println!("first_val = {first_val}, scores = {scores:?}");
}
first_val = 80, scores = [80, 90, 100, 110]
Storing Indices Instead of Element References
When you need to remember a position inside a Vec<T> across multiple .push() or .remove() operations, store the usize index (such as let first_idx = 0;) rather than an element reference &T. Because a usize is an independent integer, it does not borrow the Vec<T> at all!
Updating a HashMap Entry in Place (entry API)
In a HashMap<K, V>, checking if let Some(v) = map.get(&k) and then calling map.insert(k, new_v) inside the block can cause borrow conflicts or double hash lookups. Instead, use .entry(key).or_insert(...) (or .and_modify(...)), which returns a single &mut V reference to the value slot:
use std::collections::HashMap;
fn main() {
let words = ["rust", "borrow", "rust", "vec", "rust", "borrow"];
let mut counts: HashMap<&str, u32> = HashMap::new();
for word in words {
// `.entry(word).or_insert(0)` returns `&mut u32` in a single lookup:
let count_slot: &mut u32 = counts.entry(word).or_insert(0);
*count_slot += 1;
}
println!("Word counts: {counts:?}");
}
Word counts: {"rust": 3, "borrow": 2, "vec": 1}
Mutating Elements While Iterating (iter_mut and retain)
You can never call vec.push(x) or vec.remove(i) inside a for item in &vec loop because the loop header holds an active borrow of vec.
- To modify existing elements in place during a loop: Iterate over
&mut vec(for item in &mut vec { *item += 1; }). - To remove elements matching a condition in place: Call
vec.retain(|item| keep_condition):
fn main() {
let mut numbers = vec![1, -4, 7, -2, 10];
// Remove all negative numbers in place in O(n) time without borrow conflicts:
numbers.retain(|&n| n >= 0);
println!("Positive numbers retained: {numbers:?}");
}
Positive numbers retained: [1, 7, 10]