Rebuild BinaryHeap on unwind from retain

This commit is contained in:
David Tolnay 2023-01-15 13:14:33 -08:00
parent 0d3eaa848c
commit fa2ff4d7e5
No known key found for this signature in database
GPG Key ID: F9BA143B95FF6D82
2 changed files with 21 additions and 7 deletions

View File

@ -851,18 +851,30 @@ impl<T: Ord> BinaryHeap<T> {
where where
F: FnMut(&T) -> bool, F: FnMut(&T) -> bool,
{ {
let mut first_removed = self.len(); struct RebuildOnDrop<'a, T: Ord> {
heap: &'a mut BinaryHeap<T>,
first_removed: usize,
}
let mut guard = RebuildOnDrop { first_removed: self.len(), heap: self };
let mut i = 0; let mut i = 0;
self.data.retain(|e| { guard.heap.data.retain(|e| {
let keep = f(e); let keep = f(e);
if !keep && i < first_removed { if !keep && i < guard.first_removed {
first_removed = i; guard.first_removed = i;
} }
i += 1; i += 1;
keep keep
}); });
// data[0..first_removed] is untouched, so we only need to rebuild the tail:
self.rebuild_tail(first_removed); impl<'a, T: Ord> Drop for RebuildOnDrop<'a, T> {
fn drop(&mut self) {
// data[..first_removed] is untouched, so we only need to
// rebuild the tail:
self.heap.rebuild_tail(self.first_removed);
}
}
} }
} }

View File

@ -488,7 +488,9 @@ fn test_retain_catch_unwind() {
}); });
})); }));
assert_eq!(heap.into_vec(), [1, 2]); // BAD!! // Naively this would be [1, 2] (an invalid heap) if BinaryHeap delegates to
// Vec's retain impl and then does not rebuild the heap after that unwinds.
assert_eq!(heap.into_vec(), [2, 1]);
} }
// old binaryheap failed this test // old binaryheap failed this test