We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership II: Borrowing and the Borrow Checker step 6 of 24
E0502: cannot borrow as mutable because also borrowed as immutable
Append a copy of a vector's first element, then return the sum of the updated vector. An empty vector stays empty and returns zero.
pub fn append_first(xs: &mut Vec<i64>) -> i64
| Input | Updated vector | Return value |
|---|---|---|
[] |
[] |
0 |
[5] |
[5, 5] |
10 |
[1, 2, 3] |
[1, 2, 3, 1] |
7 |
[-4, 1] |
[-4, 1, -4] |
-7 |
Preserve all existing elements in order. For a nonempty vector, append exactly one element, even if the first element is zero. Both the return value and the updated vector are checked.
Work with inputs whose running sum, including the appended element, fits
in i64. This exercise does not define an overflow result. Solve it without
cloning the vector or using interior mutability; the element you need is Copy.
Why the starter gets E0502
xs.first() returns Option<&i64>. The starter keeps that reference in
first, calls xs.push(*first), then reads *first again to update total.
The later read keeps the shared borrow active across push, which needs
exclusive access to the vector. Rust reports E0502 because the borrows conflict.
A push may replace the vector's allocation, so a reference into the previous
allocation could become invalid. Spare capacity does not make the conflicting
borrows acceptable to the compiler.
Copy the element before mutating
Keep an i64 value instead of a reference into the vector. In the pattern
Some(&first), the & matches the reference and first receives a copy of
the integer. Another spelling is xs.first().copied().
After the read, that integer has no borrow tying it to the vector. You can
append it and then sum the updated vector. Handle the empty case without
indexing xs[0], which would panic on an empty vector.
Here is a complete example of the same approach with a different mutation:
fn clear_and_keep_first(xs: &mut Vec<i64>) -> Option<i64> {
let first = xs.first().copied();
xs.clear();
first
}
fn main() {
let mut xs = vec![7, 8];
assert_eq!(clear_and_keep_first(&mut xs), Some(7));
assert!(xs.is_empty());
assert_eq!(clear_and_keep_first(&mut xs), None);
}
Other ways to resolve a borrow conflict
If you need a reference rather than a Copy value, check whether its last use can happen before the mutation. Many borrows end at their last required use, without needing a new block. A returned reference or a destructor that uses borrowed data can keep a borrow active longer.
For more involved changes, first compute owned results such as indices or
replacement values, then mutate the collection. Choose a collection method
that fits the task when possible: retain, for example, can remove unwanted
elements without collecting indices and repeatedly shifting the vector.
Cloning is useful when you actually need independently owned data. Types
such as RefCell support mutation through shared access with runtime borrow
checks; conflicting borrow/borrow_mut calls panic, while try_borrow
variants return errors. Neither approach is needed to read one Copy element.
Submissions must compile, pass the output checks and pass Clippy. The output checks do not inspect which borrowing strategy your implementation uses.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.