Skip to content

Commit 11368d0

Browse files
committed
Auto merge of #135 - JulianKnodt:master, r=Amanieu
Implemented drain_filter for HashMap #134 Drain filter is as described in the doc comments. The implementation is based almost entirely on retain's implementation, as per `amanieu`'s suggestion. I messed around with the lifetimes, as I'm not entirely familiar with the unsafe `iter` on the raw table, but since we're now using a lazy iterator, the predicate must be valid for as long as the borrow on the table. I also annotated the function with a `#[must_use]`, otherwise the drain would have no effect. Please let me know if there are any other additions before this change can be added. Thanks! Edit: I also realize this could be added to hashset, let me know if I should add that as well, and if there are other tests that need to be updated.
2 parents 13027f9 + 0a65d86 commit 11368d0

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed

src/map.rs

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -956,6 +956,35 @@ where
956956
}
957957
}
958958
}
959+
/// Drains elements which are false under the given predicate,
960+
/// and returns an iterator over the removed items.
961+
///
962+
/// In other words, move all pairs `(k, v)` such that `f(&k,&mut v)` returns `false` out
963+
/// into another iterator.
964+
///
965+
/// When the returned DrainedFilter is dropped, the elements that don't satisfy
966+
/// the predicate are dropped from the table.
967+
///
968+
/// # Examples
969+
///
970+
/// ```
971+
/// use hashbrown::HashMap;
972+
///
973+
/// let mut map: HashMap<i32, i32> = (0..8).map(|x|(x, x*10)).collect();
974+
/// let drained = map.drain_filter(|&k, _| k % 2 == 0);
975+
/// assert_eq!(drained.count(), 4);
976+
/// assert_eq!(map.len(), 4);
977+
/// ```
978+
pub fn drain_filter<F>(&mut self, f: F) -> DrainFilter<'_, K, V, F>
979+
where
980+
F: FnMut(&K, &mut V) -> bool,
981+
{
982+
DrainFilter {
983+
f,
984+
iter: unsafe { self.table.iter() },
985+
table: &mut self.table,
986+
}
987+
}
959988
}
960989

961990
impl<K, V, S> HashMap<K, V, S> {
@@ -1236,6 +1265,66 @@ impl<K, V> Drain<'_, K, V> {
12361265
}
12371266
}
12381267

1268+
/// A draining iterator over entries of a `HashMap` which don't satisfy the predicate `f`.
1269+
///
1270+
/// This `struct` is created by the [`drain_filter`] method on [`HashMap`]. See its
1271+
/// documentation for more.
1272+
///
1273+
/// [`drain_filter`]: struct.HashMap.html#method.drain_filter
1274+
/// [`HashMap`]: struct.HashMap.html
1275+
pub struct DrainFilter<'a, K, V, F>
1276+
where
1277+
F: FnMut(&K, &mut V) -> bool,
1278+
{
1279+
f: F,
1280+
iter: RawIter<(K, V)>,
1281+
table: &'a mut RawTable<(K, V)>,
1282+
}
1283+
1284+
impl<'a, K, V, F> Drop for DrainFilter<'a, K, V, F>
1285+
where
1286+
F: FnMut(&K, &mut V) -> bool,
1287+
{
1288+
fn drop(&mut self) {
1289+
struct DropGuard<'r, 'a, K, V, F>(&'r mut DrainFilter<'a, K, V, F>)
1290+
where
1291+
F: FnMut(&K, &mut V) -> bool;
1292+
1293+
impl<'r, 'a, K, V, F> Drop for DropGuard<'r, 'a, K, V, F>
1294+
where
1295+
F: FnMut(&K, &mut V) -> bool,
1296+
{
1297+
fn drop(&mut self) {
1298+
while let Some(_) = self.0.next() {}
1299+
}
1300+
}
1301+
while let Some(item) = self.next() {
1302+
let guard = DropGuard(self);
1303+
drop(item);
1304+
mem::forget(guard);
1305+
}
1306+
}
1307+
}
1308+
1309+
impl<K, V, F> Iterator for DrainFilter<'_, K, V, F>
1310+
where
1311+
F: FnMut(&K, &mut V) -> bool,
1312+
{
1313+
type Item = (K, V);
1314+
fn next(&mut self) -> Option<Self::Item> {
1315+
unsafe {
1316+
while let Some(item) = self.iter.next() {
1317+
let &mut (ref key, ref mut value) = item.as_mut();
1318+
if !(self.f)(key, value) {
1319+
self.table.erase_no_drop(&item);
1320+
return Some(item.read());
1321+
}
1322+
}
1323+
}
1324+
None
1325+
}
1326+
}
1327+
12391328
/// A mutable iterator over the values of a `HashMap`.
12401329
///
12411330
/// This `struct` is created by the [`values_mut`] method on [`HashMap`]. See its
@@ -3488,6 +3577,23 @@ mod test_map {
34883577
assert_eq!(map[&6], 60);
34893578
}
34903579

3580+
#[test]
3581+
fn test_drain_filter() {
3582+
{
3583+
let mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x * 10)).collect();
3584+
let drained = map.drain_filter(|&k, _| k % 2 == 0);
3585+
let mut out = drained.collect::<Vec<_>>();
3586+
out.sort_unstable();
3587+
assert_eq!(vec![(1, 10), (3, 30), (5, 50), (7, 70)], out);
3588+
assert_eq!(map.len(), 4);
3589+
}
3590+
{
3591+
let mut map: HashMap<i32, i32> = (0..8).map(|x| (x, x * 10)).collect();
3592+
drop(map.drain_filter(|&k, _| k % 2 == 0));
3593+
assert_eq!(map.len(), 4);
3594+
}
3595+
}
3596+
34913597
#[test]
34923598
#[cfg_attr(miri, ignore)] // FIXME: no OOM signalling (https://github.com/rust-lang/miri/issues/613)
34933599
fn test_try_reserve() {

0 commit comments

Comments
 (0)