Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
89 changes: 85 additions & 4 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -950,6 +950,22 @@ impl<K, V, S, A: Allocator> HashMap<K, V, S, A> {
{
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,
Expand Down Expand Up @@ -2579,7 +2595,7 @@ impl<K, V, A: Allocator> 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<K, V, F, A> Iterator for ExtractIf<'_, K, V, F, A>
Expand All @@ -2591,17 +2607,82 @@ where

#[cfg_attr(feature = "inline-more", inline)]
fn next(&mut self) -> Option<Self::Item> {
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<usize>) {
(0, self.inner.iter.size_hint().1)
self.inner.size_hint()
}
}

impl<K, V, F> 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<usize>) {
/// 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::<Vec<_>>(), &[(1, 2)]);
/// assert_eq!(map.iter().collect::<Vec<_>>(), &[(&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<F>(&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<usize>) {
(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`.
///
Expand Down
12 changes: 4 additions & 8 deletions src/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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!)
// =============================
Expand Down Expand Up @@ -400,10 +399,7 @@ impl<T, S, A: Allocator> HashSet<T, S, A> {
{
ExtractIf {
f,
inner: RawExtractIf {
iter: unsafe { self.map.table.iter() },
table: &mut self.map.table,
},
inner: self.map.extractor(),
}
}

Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -1885,13 +1881,13 @@ where
#[cfg_attr(feature = "inline-more", inline)]
fn next(&mut self) -> Option<Self::Item> {
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<usize>) {
(0, self.inner.iter.size_hint().1)
self.inner.size_hint()
}
}

Expand Down
93 changes: 90 additions & 3 deletions src/table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -3150,7 +3165,7 @@ impl<T: fmt::Debug, A: Allocator> 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<T, F, A: Allocator> Iterator for ExtractIf<'_, T, F, A>
Expand All @@ -3161,17 +3176,89 @@ where

#[inline]
fn next(&mut self) -> Option<Self::Item> {
self.inner.next(|val| (self.f)(val))
self.inner.next(&mut self.f)
}

#[inline]
fn size_hint(&self) -> (usize, Option<usize>) {
(0, self.inner.iter.size_hint().1)
self.inner.size_hint()
}
}

impl<T, F, A: Allocator> 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<T> {
/// self.inner.next(|val| (self.f)(val))
/// }
/// fn size_hint(&self) -> (usize, Option<usize>) {
/// 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::<Vec<_>>(), &[1]);
/// assert_eq!(table.iter().collect::<Vec<_>>(), &[&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`].
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This needs a more complete description of what the closure parameter means and what the Some/None return values mean.

pub fn next<F>(&mut self, f: F) -> Option<T>
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<usize>) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since we can always precisely determined how many items remain, this should be called remaining_elements or something similar and just return a usize.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's fair; my main thought process here was to make the API as close to Iterator as possible minus the closure. I'll make that change.

(0, self.inner.iter.size_hint().1)
}
}

#[cfg(test)]
mod tests {
use super::HashTable;
Expand Down