diff --git a/src/map.rs b/src/map.rs index 2154ec3e1..404554803 100644 --- a/src/map.rs +++ b/src/map.rs @@ -950,6 +950,22 @@ impl HashMap { { ExtractIf { f, + inner: self.extractor(), + } + } + + /// Returns an iterator-like struct which can be used to extract entries + /// from the map based upon a function. + /// + /// This is required to implement [`ExtractIf`] efficiently for derived + /// containers because our current MSRV does not allow naming the types of + /// closures. + /// + /// See [`Extractor`] for examples on how to efficiently use this + /// iterator. + #[cfg_attr(feature = "inline-more", inline)] + pub fn extractor(&mut self) -> Extractor<'_, K, V, A> { + Extractor { inner: RawExtractIf { iter: unsafe { self.table.iter() }, table: &mut self.table, @@ -2579,7 +2595,7 @@ impl Drain<'_, K, V, A> { #[must_use = "Iterators are lazy unless consumed"] pub struct ExtractIf<'a, K, V, F, A: Allocator = Global> { f: F, - inner: RawExtractIf<'a, (K, V), A>, + inner: Extractor<'a, K, V, A>, } impl Iterator for ExtractIf<'_, K, V, F, A> @@ -2591,17 +2607,82 @@ where #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option { - self.inner.next(|&mut (ref k, ref mut v)| (self.f)(k, v)) + self.inner.next(&mut self.f) } #[inline] fn size_hint(&self) -> (usize, Option) { - (0, self.inner.iter.size_hint().1) + self.inner.size_hint() } } - impl FusedIterator for ExtractIf<'_, K, V, F> where F: FnMut(&K, &mut V) -> bool {} +/// An iterator-like struct which can be used to extract entries from a map +/// based upon a function. +/// +/// This is required to implement [`ExtractIf`] efficiently for derived +/// containers because our current MSRV does not allow naming the types of +/// closures. +/// +/// This `struct` is created by [`HashMap::extractor`]. +/// +/// # Examples +/// +/// We can use the extractor to create a version of [`ExtractIf`] which passes +/// an immutable reference instead of a mutable one: +/// +/// ``` +/// use std::iter::FusedIterator; +/// use hashbrown::HashMap; +/// use hashbrown::hash_map::Extractor; +/// +/// struct ExtractIfImmutable<'a, K, V, F> { +/// inner: Extractor<'a, K, V>, +/// f: F, +/// } +/// impl<'a, K, V, F> Iterator for ExtractIfImmutable<'a, K, V, F> +/// where +/// F: FnMut(&K, &V) -> bool +/// { +/// type Item = (K, V); +/// fn next(&mut self) -> Option<(K, V)> { +/// self.inner.next(|key, val| (self.f)(key, val)) +/// } +/// fn size_hint(&self) -> (usize, Option) { +/// self.inner.size_hint() +/// } +/// } +/// impl<'a, K, V, F> FusedIterator for ExtractIfImmutable<'a, K, V, F> +/// where +/// F: FnMut(&K, &V) -> bool +/// {} +/// +/// let mut map = HashMap::from([(1, 2), (2, 1)]); +/// let mut iter = ExtractIfImmutable { inner: map.extractor(), f: |k: &u32, v: &u32| *k == 1 }; +/// assert_eq!(iter.collect::>(), &[(1, 2)]); +/// assert_eq!(map.iter().collect::>(), &[(&2, &1)]); +/// ``` +#[must_use = "Iterators are lazy unless consumed"] +pub struct Extractor<'a, K, V, A: Allocator = Global> { + inner: RawExtractIf<'a, (K, V), A>, +} +impl<'a, K, V, A: Allocator> Extractor<'a, K, V, A> { + /// Extracts elements from the table based upon a function. + /// + /// This can be used to implement [`Iterator::next`]. + pub fn next(&mut self, mut f: F) -> Option<(K, V)> + where + F: FnMut(&K, &mut V) -> bool, + { + self.inner.next(|(k, v)| f(k, v)) + } + + /// Returns the equivalent of [`Iterator::size_hint`] for the extractor. + pub fn size_hint(&self) -> (usize, Option) { + (0, self.inner.iter.size_hint().1) + } +} + /// A mutable iterator over the values of a `HashMap` in arbitrary order. /// The iterator element type is `&'a mut V`. /// diff --git a/src/set.rs b/src/set.rs index 366a85af6..a00e2824c 100644 --- a/src/set.rs +++ b/src/set.rs @@ -8,7 +8,6 @@ use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, use super::map::{self, HashMap, Keys}; use crate::DefaultHashBuilder; use crate::alloc::{Allocator, Global}; -use crate::raw::RawExtractIf; // Future Optimization (FIXME!) // ============================= @@ -400,10 +399,7 @@ impl HashSet { { ExtractIf { f, - inner: RawExtractIf { - iter: unsafe { self.map.table.iter() }, - table: &mut self.map.table, - }, + inner: self.map.extractor(), } } @@ -1649,7 +1645,7 @@ pub struct Drain<'a, K, A: Allocator = Global> { #[must_use = "Iterators are lazy unless consumed"] pub struct ExtractIf<'a, K, F, A: Allocator = Global> { f: F, - inner: RawExtractIf<'a, (K, ()), A>, + inner: map::Extractor<'a, K, (), A>, } /// A lazy iterator producing elements in the intersection of `HashSet`s. @@ -1885,13 +1881,13 @@ where #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option { self.inner - .next(|&mut (ref k, ())| (self.f)(k)) + .next(|k, _: &mut ()| (self.f)(k)) .map(|(k, ())| k) } #[inline] fn size_hint(&self) -> (usize, Option) { - (0, self.inner.iter.size_hint().1) + self.inner.size_hint() } } diff --git a/src/table.rs b/src/table.rs index 094350442..85b6c76c1 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1382,6 +1382,21 @@ where { ExtractIf { f, + inner: self.extractor(), + } + } + + /// Returns an iterator-like struct which can be used to extract elements + /// from the table based upon a function. + /// + /// This is required to implement [`ExtractIf`] efficiently for derived + /// containers because our current MSRV does not allow naming the types of + /// closures. + /// + /// See [`Extractor`] for examples on how to efficiently use this + /// iterator. + pub fn extractor(&mut self) -> Extractor<'_, T, A> { + Extractor { inner: RawExtractIf { iter: unsafe { self.raw.iter() }, table: &mut self.raw, @@ -3150,7 +3165,7 @@ impl fmt::Debug for Drain<'_, T, A> { #[must_use = "Iterators are lazy unless consumed"] pub struct ExtractIf<'a, T, F, A: Allocator = Global> { f: F, - inner: RawExtractIf<'a, T, A>, + inner: Extractor<'a, T, A>, } impl Iterator for ExtractIf<'_, T, F, A> @@ -3161,17 +3176,89 @@ where #[inline] fn next(&mut self) -> Option { - self.inner.next(|val| (self.f)(val)) + self.inner.next(&mut self.f) } #[inline] fn size_hint(&self) -> (usize, Option) { - (0, self.inner.iter.size_hint().1) + self.inner.size_hint() } } impl FusedIterator for ExtractIf<'_, T, F, A> where F: FnMut(&mut T) -> bool {} +/// An iterator-like struct which can be used to extract elements from a table +/// based upon a function. +/// +/// This is required to implement [`ExtractIf`] efficiently for derived +/// containers because our current MSRV does not allow naming the types of +/// closures. +/// +/// This `struct` is created by [`HashTable::extractor`]. +/// +/// # Examples +/// +/// We can use the extractor to create a version of [`ExtractIf`] which passes +/// an immutable reference instead of a mutable one: +/// +/// ``` +/// use std::hash::BuildHasher; +/// use std::iter::FusedIterator; +/// use hashbrown::{DefaultHashBuilder, HashTable}; +/// use hashbrown::hash_table::Extractor; +/// +/// struct ExtractIfImmutable<'a, T, F> { +/// inner: Extractor<'a, T>, +/// f: F, +/// } +/// impl<'a, T, F> Iterator for ExtractIfImmutable<'a, T, F> +/// where +/// F: FnMut(&T) -> bool +/// { +/// type Item = T; +/// fn next(&mut self) -> Option { +/// self.inner.next(|val| (self.f)(val)) +/// } +/// fn size_hint(&self) -> (usize, Option) { +/// self.inner.size_hint() +/// } +/// } +/// impl<'a, T, F> FusedIterator for ExtractIfImmutable<'a, T, F> +/// where +/// F: FnMut(&T) -> bool +/// {} +/// +/// let mut table = HashTable::new(); +/// let hasher = DefaultHashBuilder::default(); +/// let hasher = |val: &_| hasher.hash_one(val); +/// table.insert_unique(hasher(&1), 1, hasher); +/// table.insert_unique(hasher(&2), 2, hasher); +/// +/// let mut iter = ExtractIfImmutable { inner: table.extractor(), f: |x: &u32| *x == 1 }; +/// assert_eq!(iter.collect::>(), &[1]); +/// assert_eq!(table.iter().collect::>(), &[&2]); +/// ``` +#[must_use = "Iterators are lazy unless consumed"] +pub struct Extractor<'a, T, A: Allocator = Global> { + inner: RawExtractIf<'a, T, A>, +} +impl<'a, T, A: Allocator> Extractor<'a, T, A> { + /// Extracts elements from the table based upon a function. + /// + /// This can be used to implement [`Iterator::next`]. + pub fn next(&mut self, f: F) -> Option + where + F: FnMut(&mut T) -> bool, + { + self.inner.next(f) + } + + /// Returns the equivalent of [`Iterator::size_hint`] for the extractor. + pub fn size_hint(&self) -> (usize, Option) { + (0, self.inner.iter.size_hint().1) + } +} + #[cfg(test)] mod tests { use super::HashTable;