Borrow checker rejects a loop that pushes to a vec while reading its last element Help
I have a Vec<Event> and I'm iterating over incoming items, and for each one I want to look at the last element already in the vec to decide whether to merge or push a new one. The compiler tells me I can't borrow events as mutable because it's also borrowed as immutable, which I understand in the abstract but I can't see the shape of the fix. Cloning the last element works but it's a 200 byte struct in a hot loop. What's the normal way to write this?
@borrowck_ben · 2mo ago · 3 replies
The trick is to end the immutable borrow before you start the mutable one, and the cleanest way is to compute a small decision value inside a scope and then act on it outside. Something like
let should_merge = matches!(events.last(), Some(e) if e.can_merge(&item));thenif should_merge { events.last_mut().unwrap().merge(item) } else { events.push(item) }. Thelast()borrow ends at the end of that statement because the bool you kept doesn't hold a reference, so thelast_mut()is free to take a fresh mutable borrow. No clone, no unsafe, and it reads fine.Reply
Report
@grepwitch · 2mo ago
The
unwrapbugs me slightly.if let Some(last) = events.last_mut() { ... } else { events.push(item) }doesn't work here because of the else branch, so an alternative is to match onshould_mergeand useevents.last_mut().expect("checked above")so the invariant is at least documented.Reply
Report
@scripting_slowly · 2mo ago
That's exactly it, and now the rule makes sense: keep a value not a reference. Compiles and the clone is gone.
Reply
Report