This post explains a common borrow checker error in Rust and shows how to fix it using idiomatic code.
The original Rust code shared in a code challenge was:
fn main() {
let mut vec: Vec<i32> = vec![1, 2, 3, 4, 5];
let mut sum: i32 = 0;
for i in 0..vec.len() {
let x = &vec[i].clone();
vec.push(6);
sum += *x;
}
let result: String = format!("Sum is: {}", sum.to_string());
println!("{}", result);
}
This won't compile due to a borrow checker error:
vec[i]
temporarily borrowsvec
immutably.vec.push(6)
tries to mutably borrow it at the same time.- Rust prevents this to avoid data races and undefined behavior.
Aspect | Explanation |
---|---|
clone() |
Appears safe, but the way it's written causes a temporary borrow of vec |
Borrow lifetime | The borrow from vec[i] lives too long β across the push call |
Mutation conflict | vec.push(6) needs mutable access, which conflicts with the immutable borrow |
This pattern is misleading because clone()
seems to produce an independent value, but the access to vec[i]
still creates a short-lived borrow that overlaps with the mutation.
We restructure the code to make the clone explicit before any mutation:
fn main() {
let mut vec: Vec<i32> = vec![1, 2, 3, 4, 5];
let mut sum: i32 = 0;
for i in 0..vec.len() {
let cloned = vec[i].clone(); // take the value early
vec.push(6); // mutate later β now it's safe
sum += cloned;
}
let result: String = format!("Sum is: {}", sum);
println!("{}", result);
}
- No references held across a mutation
clone()
result is owned β no borrow involved- Intent is clearer, and Rust can verify memory safety
- Clone early to avoid borrowing overlaps
- Understand that even "temporary" borrows from indexing (
vec[i]
) can interfere - Rustβs borrow checker errs on the side of safety β especially across loop iterations and mutation
- Breaking operations into clear, separate steps helps the compiler and the reader
#RustLang
#BorrowChecker
#CodeChallenge
#IdiomaticRust