Skip to content

Commit d57ccf3

Browse files
committed
Remove drain-on-drop behavior from DrainFilter
1 parent 329f86a commit d57ccf3

File tree

2 files changed

+13
-53
lines changed

2 files changed

+13
-53
lines changed

src/map.rs

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -975,12 +975,9 @@ impl<K, V, S, A: Allocator + Clone> HashMap<K, V, S, A> {
975975
/// Note that `drain_filter` lets you mutate every value in the filter closure, regardless of
976976
/// whether you choose to keep or remove it.
977977
///
978-
/// When the returned DrainedFilter is dropped, any remaining elements that satisfy
979-
/// the predicate are dropped from the table.
980-
///
981-
/// It is unspecified how many more elements will be subjected to the closure
982-
/// if a panic occurs in the closure, or a panic occurs while dropping an element,
983-
/// or if the `DrainFilter` value is leaked.
978+
/// If the returned `DrainFilter` is not exhausted, e.g. because it is dropped without iterating
979+
/// or the iteration short-circuits, then the remaining elements will be retained.
980+
/// Use [`retain()`] with a negated predicate if you do not need the returned iterator.
984981
///
985982
/// Keeps the allocated memory for reuse.
986983
///
@@ -1007,9 +1004,8 @@ impl<K, V, S, A: Allocator + Clone> HashMap<K, V, S, A> {
10071004
/// let d = map.drain_filter(|k, _v| k % 2 != 0);
10081005
/// }
10091006
///
1010-
/// // But the map lens have been reduced by half
1011-
/// // even if we do not use DrainFilter iterator.
1012-
/// assert_eq!(map.len(), 4);
1007+
/// // DrainFilter was not exhausted, therefore no elements were drained.
1008+
/// assert_eq!(map.len(), 8);
10131009
/// ```
10141010
#[cfg_attr(feature = "inline-more", inline)]
10151011
pub fn drain_filter<F>(&mut self, f: F) -> DrainFilter<'_, K, V, F, A>
@@ -2760,6 +2756,7 @@ impl<K, V, A: Allocator + Clone> Drain<'_, K, V, A> {
27602756
///
27612757
/// assert_eq!(map.len(), 1);
27622758
/// ```
2759+
#[must_use = "Iterators are lazy unless consumed"]
27632760
pub struct DrainFilter<'a, K, V, F, A: Allocator + Clone = Global>
27642761
where
27652762
F: FnMut(&K, &mut V) -> bool,
@@ -2768,30 +2765,6 @@ where
27682765
inner: DrainFilterInner<'a, K, V, A>,
27692766
}
27702767

2771-
impl<'a, K, V, F, A> Drop for DrainFilter<'a, K, V, F, A>
2772-
where
2773-
F: FnMut(&K, &mut V) -> bool,
2774-
A: Allocator + Clone,
2775-
{
2776-
#[cfg_attr(feature = "inline-more", inline)]
2777-
fn drop(&mut self) {
2778-
while let Some(item) = self.next() {
2779-
let guard = ConsumeAllOnDrop(self);
2780-
drop(item);
2781-
mem::forget(guard);
2782-
}
2783-
}
2784-
}
2785-
2786-
pub(super) struct ConsumeAllOnDrop<'a, T: Iterator>(pub &'a mut T);
2787-
2788-
impl<T: Iterator> Drop for ConsumeAllOnDrop<'_, T> {
2789-
#[cfg_attr(feature = "inline-more", inline)]
2790-
fn drop(&mut self) {
2791-
self.0.for_each(drop);
2792-
}
2793-
}
2794-
27952768
impl<K, V, F, A> Iterator for DrainFilter<'_, K, V, F, A>
27962769
where
27972770
F: FnMut(&K, &mut V) -> bool,
@@ -8180,7 +8153,7 @@ mod test_map {
81808153
}
81818154
{
81828155
let mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x * 10)).collect();
8183-
drop(map.drain_filter(|&k, _| k % 2 == 0));
8156+
map.drain_filter(|&k, _| k % 2 == 0).for_each(drop);
81848157
assert_eq!(map.len(), 4);
81858158
}
81868159
}

src/set.rs

Lines changed: 6 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ use alloc::borrow::ToOwned;
55
use core::fmt;
66
use core::hash::{BuildHasher, Hash};
77
use core::iter::{Chain, FromIterator, FusedIterator};
8-
use core::mem;
98
use core::ops::{BitAnd, BitOr, BitXor, Sub};
109

11-
use super::map::{self, ConsumeAllOnDrop, DefaultHashBuilder, DrainFilterInner, HashMap, Keys};
10+
use super::map::{self, DefaultHashBuilder, DrainFilterInner, HashMap, Keys};
1211
use crate::raw::{Allocator, Global};
1312

1413
// Future Optimization (FIXME!)
@@ -380,8 +379,9 @@ impl<T, S, A: Allocator + Clone> HashSet<T, S, A> {
380379
/// In other words, move all elements `e` such that `f(&e)` returns `true` out
381380
/// into another iterator.
382381
///
383-
/// When the returned DrainedFilter is dropped, any remaining elements that satisfy
384-
/// the predicate are dropped from the set.
382+
/// If the returned `DrainFilter` is not exhausted, e.g. because it is dropped without iterating
383+
/// or the iteration short-circuits, then the remaining elements will be retained.
384+
/// Use [`retain()`] with a negated predicate if you do not need the returned iterator.
385385
///
386386
/// # Examples
387387
///
@@ -1572,6 +1572,7 @@ pub struct Drain<'a, K, A: Allocator + Clone = Global> {
15721572
///
15731573
/// [`drain_filter`]: struct.HashSet.html#method.drain_filter
15741574
/// [`HashSet`]: struct.HashSet.html
1575+
#[must_use = "Iterators are lazy unless consumed"]
15751576
pub struct DrainFilter<'a, K, F, A: Allocator + Clone = Global>
15761577
where
15771578
F: FnMut(&K) -> bool,
@@ -1768,20 +1769,6 @@ impl<K: fmt::Debug, A: Allocator + Clone> fmt::Debug for Drain<'_, K, A> {
17681769
}
17691770
}
17701771

1771-
impl<'a, K, F, A: Allocator + Clone> Drop for DrainFilter<'a, K, F, A>
1772-
where
1773-
F: FnMut(&K) -> bool,
1774-
{
1775-
#[cfg_attr(feature = "inline-more", inline)]
1776-
fn drop(&mut self) {
1777-
while let Some(item) = self.next() {
1778-
let guard = ConsumeAllOnDrop(self);
1779-
drop(item);
1780-
mem::forget(guard);
1781-
}
1782-
}
1783-
}
1784-
17851772
impl<K, F, A: Allocator + Clone> Iterator for DrainFilter<'_, K, F, A>
17861773
where
17871774
F: FnMut(&K) -> bool,
@@ -2861,7 +2848,7 @@ mod test_set {
28612848
}
28622849
{
28632850
let mut set: HashSet<i32> = (0..8).collect();
2864-
drop(set.drain_filter(|&k| k % 2 == 0));
2851+
set.drain_filter(|&k| k % 2 == 0).for_each(drop);
28652852
assert_eq!(set.len(), 4, "Removes non-matching items on drop");
28662853
}
28672854
}

0 commit comments

Comments
 (0)