Skip to content

Commit fa2ff4d

Browse files
committed
Rebuild BinaryHeap on unwind from retain
1 parent 0d3eaa8 commit fa2ff4d

File tree

2 files changed

+21
-7
lines changed

2 files changed

+21
-7
lines changed

library/alloc/src/collections/binary_heap/mod.rs

+18-6
Original file line numberDiff line numberDiff line change
@@ -851,18 +851,30 @@ impl<T: Ord> BinaryHeap<T> {
851851
where
852852
F: FnMut(&T) -> bool,
853853
{
854-
let mut first_removed = self.len();
854+
struct RebuildOnDrop<'a, T: Ord> {
855+
heap: &'a mut BinaryHeap<T>,
856+
first_removed: usize,
857+
}
858+
859+
let mut guard = RebuildOnDrop { first_removed: self.len(), heap: self };
860+
855861
let mut i = 0;
856-
self.data.retain(|e| {
862+
guard.heap.data.retain(|e| {
857863
let keep = f(e);
858-
if !keep && i < first_removed {
859-
first_removed = i;
864+
if !keep && i < guard.first_removed {
865+
guard.first_removed = i;
860866
}
861867
i += 1;
862868
keep
863869
});
864-
// data[0..first_removed] is untouched, so we only need to rebuild the tail:
865-
self.rebuild_tail(first_removed);
870+
871+
impl<'a, T: Ord> Drop for RebuildOnDrop<'a, T> {
872+
fn drop(&mut self) {
873+
// data[..first_removed] is untouched, so we only need to
874+
// rebuild the tail:
875+
self.heap.rebuild_tail(self.first_removed);
876+
}
877+
}
866878
}
867879
}
868880

library/alloc/src/collections/binary_heap/tests.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,9 @@ fn test_retain_catch_unwind() {
488488
});
489489
}));
490490

491-
assert_eq!(heap.into_vec(), [1, 2]); // BAD!!
491+
// Naively this would be [1, 2] (an invalid heap) if BinaryHeap delegates to
492+
// Vec's retain impl and then does not rebuild the heap after that unwinds.
493+
assert_eq!(heap.into_vec(), [2, 1]);
492494
}
493495

494496
// old binaryheap failed this test

0 commit comments

Comments
 (0)