We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Ownership II: Borrowing and the Borrow Checker step 8 of 24
The owner is frozen too: E0503, E0505, E0506
You are given a type and a function. The function does not compile.
pub struct Account {
pub id: i64,
pub balance: i64,
pub history: Vec<i64>,
}
pub fn settle(mut a: Account, deltas: &[i64]) -> (i64, Vec<i64>)
settle takes ownership of an account. For each delta in order it adds
the delta to balance and pushes the new balance onto history. It then
returns (final balance, the history vector itself) — moved out of the
account, not copied.
balance 100, history [], deltas [] -> (100, [])
balance 0, history [], deltas [-5,-10] -> (-15, [-5,-15])
balance 10, history [10], deltas [5] -> (15, [10,15])
The model you are about to lose
Most people arrive with an implicit hierarchy: the owner is in charge, and
lending something out is a courtesy that can be revoked. Under that model, if
a &a exists and the owner wants to reassign a, the owner wins.
Rust does not work that way, and these three error codes are how it tells you. Read the Reference’s phrasing from item 3.3 once more: a borrowed place “may not be mutated”; an exclusively borrowed place “may not be accessed in any way”. There is no exception carved out for the owner. A loan freezes the owner too.
Once that clicks, most of the remaining confusion in this track evaporates, because every other error is a special case of it.
The three codes, and the vocabulary they use
The diagnostics distinguish three ways of touching a place. Learning the words makes the messages readable.
E0506 — cannot assign to x because it is borrowed. You assigned. A
shared loan exists, and you wrote to the place through the owner.
let mut n = 1;
let r = &n;
n = 2; // E0506
println!("{r}");
E0505 — cannot move out of x because it is borrowed. You moved. The
place is loaned and you tried to give the value away — which would leave the
reference pointing at nothing.
let v = vec![1];
let r = &v;
let w = v; // E0505
println!("{r:?}");
E0503 — cannot use x because it was mutably borrowed. You used — even
just read. An exclusive loan exists, so every other route to the place is
closed, reading included.
let mut n = 1;
let r = &mut n;
let copy = n; // E0503
*r += 1;
Assign, move, use. Three verbs, three codes. When you see one, the first question is always the same: which loan is still alive, and why?
Which one the starter hits
The starter finishes the loop cleanly and then does this:
let acct = &a; // (1) a shared loan of the whole struct
let history = a.history; // (2) move a field out of a borrowed value -> E0505
(acct.balance, history) // (3) "borrow later used here"
Same three-span reading as always. The loan at (1) is alive at (2) because of
(3). Moving a.history out would leave acct pointing at a partially-emptied
Account, so the compiler refuses.
And notice — this is the point of the exercise — that a is the function’s
own local. It owns the account outright. Ownership does not help. The loan
is what matters.
Why the struct contains a Vec
Account holds a Vec<i64>, so it is not Copy and never can be — Vec
owns a heap allocation and duplicating it has to be an explicit, costly act.
If every field were an integer, the struct would be Copy, let history = a.history; would be a copy rather than a move, and E0505 would never fire.
That is worth filing away: whether a line is a move or a copy is a property
of the type, not of the syntax. The same let x = y; is free and harmless
for an i64 and a transfer of ownership for a Vec. This is the single
biggest source of “why does this work here and not there” in early Rust.
The fix
Do not create the loan. You need two values out of a: a Copy integer and
an owned Vec. Take them directly. A tuple expression is evaluated left to
right, so reading a.balance and then moving a.history in one expression is
fine — the read has finished before the move happens.
Cloning the history to dodge the move would compile and would be wrong: the
function’s whole contract is that the vector is handed over, and .clone() is
withdrawn in this track anyway.
Remember the grade is compile + tests + clippy -D warnings.
Loading visualization…
Stuck?
Rust reference solution
Sign in to attempt this problem and reveal the reference solution.