From 95ca829d3429c2c7cf95d3d32aee0de2eaeb6c70 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 29 Nov 2025 16:17:12 -0500 Subject: [PATCH 1/5] Replace RawTable inside HashMap with HashTable --- src/external_trait_impls/rayon/map.rs | 14 ++-- src/map.rs | 110 +++++++++++++------------- src/raw_entry.rs | 8 +- src/rustc_entry.rs | 6 +- src/set.rs | 6 +- src/table.rs | 2 + 6 files changed, 74 insertions(+), 72 deletions(-) diff --git a/src/external_trait_impls/rayon/map.rs b/src/external_trait_impls/rayon/map.rs index 9623ca747..b191bc7d4 100644 --- a/src/external_trait_impls/rayon/map.rs +++ b/src/external_trait_impls/rayon/map.rs @@ -296,7 +296,7 @@ impl HashMap { #[cfg_attr(feature = "inline-more", inline)] pub fn par_keys(&self) -> ParKeys<'_, K, V> { ParKeys { - inner: unsafe { self.table.par_iter() }, + inner: unsafe { self.table.raw.par_iter() }, marker: PhantomData, } } @@ -305,7 +305,7 @@ impl HashMap { #[cfg_attr(feature = "inline-more", inline)] pub fn par_values(&self) -> ParValues<'_, K, V> { ParValues { - inner: unsafe { self.table.par_iter() }, + inner: unsafe { self.table.raw.par_iter() }, marker: PhantomData, } } @@ -316,7 +316,7 @@ impl HashMap { #[cfg_attr(feature = "inline-more", inline)] pub fn par_values_mut(&mut self) -> ParValuesMut<'_, K, V> { ParValuesMut { - inner: unsafe { self.table.par_iter() }, + inner: unsafe { self.table.raw.par_iter() }, marker: PhantomData, } } @@ -326,7 +326,7 @@ impl HashMap { #[cfg_attr(feature = "inline-more", inline)] pub fn par_drain(&mut self) -> ParDrain<'_, K, V, A> { ParDrain { - inner: self.table.par_drain(), + inner: self.table.raw.par_drain(), } } } @@ -357,7 +357,7 @@ impl IntoParallelIterator for HashMap< #[cfg_attr(feature = "inline-more", inline)] fn into_par_iter(self) -> Self::Iter { IntoParIter { - inner: self.table.into_par_iter(), + inner: self.table.raw.into_par_iter(), } } } @@ -369,7 +369,7 @@ impl<'a, K: Sync, V: Sync, S, A: Allocator> IntoParallelIterator for &'a HashMap #[cfg_attr(feature = "inline-more", inline)] fn into_par_iter(self) -> Self::Iter { ParIter { - inner: unsafe { self.table.par_iter() }, + inner: unsafe { self.table.raw.par_iter() }, marker: PhantomData, } } @@ -382,7 +382,7 @@ impl<'a, K: Sync, V: Send, S, A: Allocator> IntoParallelIterator for &'a mut Has #[cfg_attr(feature = "inline-more", inline)] fn into_par_iter(self) -> Self::Iter { ParIterMut { - inner: unsafe { self.table.par_iter() }, + inner: unsafe { self.table.raw.par_iter() }, marker: PhantomData, } } diff --git a/src/map.rs b/src/map.rs index f37bafa02..d61d40737 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,7 +1,5 @@ -use crate::raw::{ - Allocator, Bucket, Global, RawDrain, RawExtractIf, RawIntoIter, RawIter, RawTable, -}; -use crate::{DefaultHashBuilder, Equivalent, TryReserveError}; +use crate::raw::{Allocator, Bucket, Global, RawDrain, RawExtractIf, RawIntoIter, RawIter}; +use crate::{DefaultHashBuilder, Equivalent, HashTable, TryReserveError}; use ::alloc::borrow::ToOwned; use core::borrow::Borrow; use core::fmt::{self, Debug}; @@ -185,7 +183,7 @@ pub use crate::raw_entry::*; /// ``` pub struct HashMap { pub(crate) hash_builder: S, - pub(crate) table: RawTable<(K, V), A>, + pub(crate) table: HashTable<(K, V), A>, } impl Clone for HashMap { @@ -205,7 +203,7 @@ impl Clone for HashMap(hash_builder: &S) -> impl Fn(&(Q, V)) -> u64 + '_ where @@ -216,7 +214,7 @@ where } /// Ensures that a single closure type across uses of this which, in turn prevents multiple -/// instances of any functions like `RawTable::reserve` from being generated +/// instances of any functions like `HashTable::reserve` from being generated #[cfg_attr(feature = "inline-more", inline)] pub(crate) fn equivalent_key(k: &Q) -> impl Fn(&(K, V)) -> bool + '_ where @@ -226,7 +224,7 @@ where } /// Ensures that a single closure type across uses of this which, in turn prevents multiple -/// instances of any functions like `RawTable::reserve` from being generated +/// instances of any functions like `HashTable::reserve` from being generated #[cfg_attr(feature = "inline-more", inline)] #[allow(dead_code)] pub(crate) fn equivalent(k: &Q) -> impl Fn(&K) -> bool + '_ @@ -458,7 +456,7 @@ impl HashMap { pub const fn with_hasher(hash_builder: S) -> Self { Self { hash_builder, - table: RawTable::new(), + table: HashTable::new(), } } @@ -500,7 +498,7 @@ impl HashMap { pub fn with_capacity_and_hasher(capacity: usize, hash_builder: S) -> Self { Self { hash_builder, - table: RawTable::with_capacity(capacity), + table: HashTable::with_capacity(capacity), } } } @@ -509,7 +507,7 @@ impl HashMap { /// Returns a reference to the underlying allocator. #[inline] pub fn allocator(&self) -> &A { - self.table.allocator() + self.table.raw.allocator() } /// Creates an empty `HashMap` which will use the given hash builder to hash @@ -544,7 +542,7 @@ impl HashMap { pub const fn with_hasher_in(hash_builder: S, alloc: A) -> Self { Self { hash_builder, - table: RawTable::new_in(alloc), + table: HashTable::new_in(alloc), } } @@ -579,7 +577,7 @@ impl HashMap { pub fn with_capacity_and_hasher_in(capacity: usize, hash_builder: S, alloc: A) -> Self { Self { hash_builder, - table: RawTable::with_capacity_in(capacity, alloc), + table: HashTable::with_capacity_in(capacity, alloc), } } @@ -617,7 +615,7 @@ impl HashMap { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn capacity(&self) -> usize { - self.table.capacity() + self.table.raw.capacity() } /// An iterator visiting all keys in arbitrary order. @@ -756,7 +754,7 @@ impl HashMap { // Here we tie the lifetime of self to the iter. unsafe { Iter { - inner: self.table.iter(), + inner: self.table.raw.iter(), marker: PhantomData, } } @@ -801,7 +799,7 @@ impl HashMap { // Here we tie the lifetime of self to the iter. unsafe { IterMut { - inner: self.table.iter(), + inner: self.table.raw.iter(), marker: PhantomData, } } @@ -810,7 +808,7 @@ impl HashMap { #[cfg(test)] #[cfg_attr(feature = "inline-more", inline)] fn raw_capacity(&self) -> usize { - self.table.num_buckets() + self.table.raw.num_buckets() } /// Returns the number of elements in the map. @@ -827,7 +825,7 @@ impl HashMap { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn len(&self) -> usize { - self.table.len() + self.table.raw.len() } /// Returns `true` if the map contains no elements. @@ -888,7 +886,7 @@ impl HashMap { #[cfg_attr(feature = "inline-more", inline)] pub fn drain(&mut self) -> Drain<'_, K, V, A> { Drain { - inner: self.table.drain(), + inner: self.table.raw.drain(), } } @@ -921,10 +919,10 @@ impl HashMap { { // Here we only use `iter` as a temporary, preventing use-after-free unsafe { - for item in self.table.iter() { + for item in self.table.raw.iter() { let &mut (ref key, ref mut value) = item.as_mut(); if !f(key, value) { - self.table.erase(item); + self.table.raw.erase(item); } } } @@ -981,8 +979,8 @@ impl HashMap { ExtractIf { f, inner: RawExtractIf { - iter: unsafe { self.table.iter() }, - table: &mut self.table, + iter: unsafe { self.table.raw.iter() }, + table: &mut self.table.raw, }, } } @@ -1008,7 +1006,7 @@ impl HashMap { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn clear(&mut self) { - self.table.clear(); + self.table.raw.clear(); } /// Creates a consuming iterator visiting all the keys in arbitrary order. @@ -1229,7 +1227,7 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn entry(&mut self, key: K) -> Entry<'_, K, V, S, A> { let hash = make_hash::(&self.hash_builder, &key); - if let Some(elem) = self.table.find(hash, equivalent_key(&key)) { + if let Some(elem) = self.table.raw.find(hash, equivalent_key(&key)) { Entry::Occupied(OccupiedEntry { hash, elem, @@ -1267,7 +1265,7 @@ where Q: Hash + Equivalent + ?Sized, { let hash = make_hash::(&self.hash_builder, key); - if let Some(elem) = self.table.find(hash, equivalent_key(key)) { + if let Some(elem) = self.table.raw.find(hash, equivalent_key(key)) { EntryRef::Occupied(OccupiedEntry { hash, elem, @@ -1307,9 +1305,9 @@ where Q: Hash + Equivalent + ?Sized, { // Avoid `Option::map` because it bloats LLVM IR. - if !self.table.is_empty() { + if !self.table.raw.is_empty() { let hash = make_hash::(&self.hash_builder, k); - match self.table.get(hash, equivalent_key(k)) { + match self.table.raw.get(hash, equivalent_key(k)) { Some((_, v)) => Some(v), None => None, } @@ -1343,9 +1341,9 @@ where Q: Hash + Equivalent + ?Sized, { // Avoid `Option::map` because it bloats LLVM IR. - if !self.table.is_empty() { + if !self.table.raw.is_empty() { let hash = make_hash::(&self.hash_builder, k); - match self.table.get(hash, equivalent_key(k)) { + match self.table.raw.get(hash, equivalent_key(k)) { Some((key, value)) => Some((key, value)), None => None, } @@ -1383,9 +1381,9 @@ where Q: Hash + Equivalent + ?Sized, { // Avoid `Option::map` because it bloats LLVM IR. - if !self.table.is_empty() { + if !self.table.raw.is_empty() { let hash = make_hash::(&self.hash_builder, k); - match self.table.get_mut(hash, equivalent_key(k)) { + match self.table.raw.get_mut(hash, equivalent_key(k)) { Some(&mut (ref key, ref mut value)) => Some((key, value)), None => None, } @@ -1418,9 +1416,9 @@ where where Q: Hash + Equivalent + ?Sized, { - if !self.table.is_empty() { + if !self.table.raw.is_empty() { let hash = make_hash::(&self.hash_builder, k); - self.table.get(hash, equivalent_key(k)).is_some() + self.table.raw.get(hash, equivalent_key(k)).is_some() } else { false } @@ -1455,9 +1453,9 @@ where Q: Hash + Equivalent + ?Sized, { // Avoid `Option::map` because it bloats LLVM IR. - if !self.table.is_empty() { + if !self.table.raw.is_empty() { let hash = make_hash::(&self.hash_builder, k); - match self.table.get_mut(hash, equivalent_key(k)) { + match self.table.raw.get_mut(hash, equivalent_key(k)) { Some(&mut (_, ref mut v)) => Some(v), None => None, } @@ -1845,7 +1843,7 @@ where Ok(bucket) => Some(mem::replace(unsafe { &mut bucket.as_mut().1 }, v)), Err(index) => { unsafe { - self.table.insert_at_index(hash, index, (k, v)); + self.table.raw.insert_at_index(hash, index, (k, v)); } None } @@ -1861,7 +1859,7 @@ where where Q: Equivalent + ?Sized, { - self.table.find_or_find_insert_index( + self.table.raw.find_or_find_insert_index( hash, equivalent_key(key), make_hasher(&self.hash_builder), @@ -1928,9 +1926,10 @@ where #[cfg_attr(feature = "inline-more", inline)] pub unsafe fn insert_unique_unchecked(&mut self, k: K, v: V) -> (&K, &mut V) { let hash = make_hash::(&self.hash_builder, &k); - let bucket = self - .table - .insert(hash, (k, v), make_hasher::<_, V, S>(&self.hash_builder)); + let bucket = + self.table + .raw + .insert(hash, (k, v), make_hasher::<_, V, S>(&self.hash_builder)); let (k_ref, v_ref) = unsafe { bucket.as_mut() }; (k_ref, v_ref) } @@ -2047,7 +2046,7 @@ where Q: Hash + Equivalent + ?Sized, { let hash = make_hash::(&self.hash_builder, k); - self.table.remove_entry(hash, equivalent_key(k)) + self.table.raw.remove_entry(hash, equivalent_key(k)) } /// Returns the total amount of memory allocated internally by the hash @@ -2057,7 +2056,7 @@ where /// primarily used for memory profiling. #[inline] pub fn allocation_size(&self) -> usize { - self.table.allocation_size() + self.table.raw.allocation_size() } } @@ -3197,7 +3196,7 @@ impl IntoIterator for HashMap { #[cfg_attr(feature = "inline-more", inline)] fn into_iter(self) -> IntoIter { IntoIter { - inner: self.table.into_iter(), + inner: self.table.raw.into_iter(), } } } @@ -3875,7 +3874,7 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn remove_entry(self) -> (K, V) { - unsafe { self.table.table.remove(self.elem).0 } + unsafe { self.table.table.raw.remove(self.elem).0 } } /// Gets a reference to the value in the entry. @@ -4106,6 +4105,7 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { self.table .table + .raw .replace_bucket_with(self.elem.clone(), |(key, value)| { if let Some(new_value) = f(&key, value) { Some((key, new_value)) @@ -4186,7 +4186,7 @@ impl<'a, K, V, S, A: Allocator> VacantEntry<'a, K, V, S, A> { K: Hash, S: BuildHasher, { - let table = &mut self.table.table; + let table = &mut self.table.table.raw; let entry = table.insert_entry( self.hash, (self.key, value), @@ -4217,7 +4217,7 @@ impl<'a, K, V, S, A: Allocator> VacantEntry<'a, K, V, S, A> { K: Hash, S: BuildHasher, { - let elem = self.table.table.insert( + let elem = self.table.table.raw.insert( self.hash, (self.key, value), make_hasher::<_, V, S>(&self.table.hash_builder), @@ -4526,7 +4526,7 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, Q: ToOwned, S: BuildHasher, { - let table = &mut self.table.table; + let table = &mut self.table.table.raw; let entry = table.insert_entry( self.hash, (self.key.to_owned(), value), @@ -4597,7 +4597,7 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, Q: ToOwned, S: BuildHasher, { - let elem = self.table.table.insert( + let elem = self.table.table.raw.insert( self.hash, (self.key.to_owned(), value), make_hasher::<_, V, S>(&self.table.hash_builder), @@ -4648,7 +4648,7 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, (self.key).equivalent(&key), "key used for Entry creation is not equivalent to the one used for insertion" ); - let elem = self.table.table.insert( + let elem = self.table.table.raw.insert( self.hash, (key, value), make_hasher::<_, V, S>(&self.table.hash_builder), @@ -6494,7 +6494,7 @@ mod test_map { "panic_in_drop can be set with a type that doesn't need to be dropped", )); } - guard.table.insert( + guard.table.raw.insert( count, ( count, @@ -6664,8 +6664,8 @@ mod test_map { // the returned `RawIter / RawIterRange` iterator. assert_eq!(map.len(), 0); assert_eq!(map.iter().count(), 0); - assert_eq!(unsafe { map.table.iter().count() }, 0); - assert_eq!(unsafe { map.table.iter().iter.count() }, 0); + assert_eq!(unsafe { map.table.raw.iter().count() }, 0); + assert_eq!(unsafe { map.table.raw.iter().iter.count() }, 0); for idx in 0..map.table.num_buckets() { let idx = idx as u64; @@ -6732,8 +6732,8 @@ mod test_map { // the returned `RawIter / RawIterRange` iterator. assert_eq!(map.len(), 0); assert_eq!(map.iter().count(), 0); - assert_eq!(unsafe { map.table.iter().count() }, 0); - assert_eq!(unsafe { map.table.iter().iter.count() }, 0); + assert_eq!(unsafe { map.table.raw.iter().count() }, 0); + assert_eq!(unsafe { map.table.raw.iter().iter.count() }, 0); for idx in 0..map.table.num_buckets() { let idx = idx as u64; diff --git a/src/raw_entry.rs b/src/raw_entry.rs index 20623a83b..69fe08962 100644 --- a/src/raw_entry.rs +++ b/src/raw_entry.rs @@ -600,14 +600,14 @@ impl<'a, K, V, S, A: Allocator> RawEntryBuilderMut<'a, K, V, S, A> { where for<'b> F: FnMut(&'b K) -> bool, { - match self.map.table.find(hash, |(k, _)| is_match(k)) { + match self.map.table.raw.find(hash, |(k, _)| is_match(k)) { Some(elem) => RawEntryMut::Occupied(RawOccupiedEntryMut { elem, - table: &mut self.map.table, + table: &mut self.map.table.raw, hash_builder: &self.map.hash_builder, }), None => RawEntryMut::Vacant(RawVacantEntryMut { - table: &mut self.map.table, + table: &mut self.map.table.raw, hash_builder: &self.map.hash_builder, }), } @@ -671,7 +671,7 @@ impl<'a, K, V, S, A: Allocator> RawEntryBuilder<'a, K, V, S, A> { where F: FnMut(&K) -> bool, { - match self.map.table.get(hash, |(k, _)| is_match(k)) { + match self.map.table.raw.get(hash, |(k, _)| is_match(k)) { Some((key, value)) => Some((key, value)), None => None, } diff --git a/src/rustc_entry.rs b/src/rustc_entry.rs index 233fe7a2d..446bac151 100644 --- a/src/rustc_entry.rs +++ b/src/rustc_entry.rs @@ -33,10 +33,10 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn rustc_entry(&mut self, key: K) -> RustcEntry<'_, K, V, A> { let hash = make_hash(&self.hash_builder, &key); - if let Some(elem) = self.table.find(hash, |q| q.0.eq(&key)) { + if let Some(elem) = self.table.raw.find(hash, |q| q.0.eq(&key)) { RustcEntry::Occupied(RustcOccupiedEntry { elem, - table: &mut self.table, + table: &mut self.table.raw, }) } else { // Ideally we would put this in VacantEntry::insert, but Entry is not @@ -47,7 +47,7 @@ where RustcEntry::Vacant(RustcVacantEntry { hash, key, - table: &mut self.table, + table: &mut self.table.raw, }) } } diff --git a/src/set.rs b/src/set.rs index 36fb60a36..5718231b6 100644 --- a/src/set.rs +++ b/src/set.rs @@ -408,8 +408,8 @@ impl HashSet { ExtractIf { f, inner: RawExtractIf { - iter: unsafe { self.map.table.iter() }, - table: &mut self.map.table, + iter: unsafe { self.map.table.raw.iter() }, + table: &mut self.map.table.raw, }, } } @@ -1139,7 +1139,7 @@ where Ok(bucket) => Some(mem::replace(unsafe { &mut bucket.as_mut().0 }, value)), Err(index) => { unsafe { - self.map.table.insert_at_index(hash, index, (value, ())); + self.map.table.raw.insert_at_index(hash, index, (value, ())); } None } diff --git a/src/table.rs b/src/table.rs index a890e29c2..6b28b7902 100644 --- a/src/table.rs +++ b/src/table.rs @@ -66,6 +66,7 @@ impl HashTable { /// assert_eq!(table.len(), 0); /// assert_eq!(table.capacity(), 0); /// ``` + #[cfg_attr(feature = "rustc-dep-of-std", rustc_const_stable_indirect)] pub const fn new() -> Self { Self { raw: RawTable::new(), @@ -133,6 +134,7 @@ where /// # test() /// # } /// ``` + #[cfg_attr(feature = "rustc-dep-of-std", rustc_const_stable_indirect)] pub const fn new_in(alloc: A) -> Self { Self { raw: RawTable::new_in(alloc), From 629f2494031a4c1b4f6c149c7d4cbf2ae8e9a888 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 29 Nov 2025 16:25:54 -0500 Subject: [PATCH 2/5] Call HashTable methods where exact matches exist --- src/map.rs | 43 ++++++++++++++++++------------------------- 1 file changed, 18 insertions(+), 25 deletions(-) diff --git a/src/map.rs b/src/map.rs index d61d40737..a4d324fb8 100644 --- a/src/map.rs +++ b/src/map.rs @@ -507,7 +507,7 @@ impl HashMap { /// Returns a reference to the underlying allocator. #[inline] pub fn allocator(&self) -> &A { - self.table.raw.allocator() + self.table.allocator() } /// Creates an empty `HashMap` which will use the given hash builder to hash @@ -615,7 +615,7 @@ impl HashMap { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn capacity(&self) -> usize { - self.table.raw.capacity() + self.table.capacity() } /// An iterator visiting all keys in arbitrary order. @@ -808,7 +808,7 @@ impl HashMap { #[cfg(test)] #[cfg_attr(feature = "inline-more", inline)] fn raw_capacity(&self) -> usize { - self.table.raw.num_buckets() + self.table.num_buckets() } /// Returns the number of elements in the map. @@ -825,7 +825,7 @@ impl HashMap { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn len(&self) -> usize { - self.table.raw.len() + self.table.len() } /// Returns `true` if the map contains no elements. @@ -917,15 +917,8 @@ impl HashMap { where F: FnMut(&K, &mut V) -> bool, { - // Here we only use `iter` as a temporary, preventing use-after-free - unsafe { - for item in self.table.raw.iter() { - let &mut (ref key, ref mut value) = item.as_mut(); - if !f(key, value) { - self.table.raw.erase(item); - } - } - } + self.table + .retain(move |&mut (ref key, ref mut val)| f(key, val)) } /// Drains elements which are true under the given predicate, @@ -1006,7 +999,7 @@ impl HashMap { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn clear(&mut self) { - self.table.raw.clear(); + self.table.clear(); } /// Creates a consuming iterator visiting all the keys in arbitrary order. @@ -1305,9 +1298,9 @@ where Q: Hash + Equivalent + ?Sized, { // Avoid `Option::map` because it bloats LLVM IR. - if !self.table.raw.is_empty() { + if !self.table.is_empty() { let hash = make_hash::(&self.hash_builder, k); - match self.table.raw.get(hash, equivalent_key(k)) { + match self.table.find(hash, equivalent_key(k)) { Some((_, v)) => Some(v), None => None, } @@ -1341,9 +1334,9 @@ where Q: Hash + Equivalent + ?Sized, { // Avoid `Option::map` because it bloats LLVM IR. - if !self.table.raw.is_empty() { + if !self.table.is_empty() { let hash = make_hash::(&self.hash_builder, k); - match self.table.raw.get(hash, equivalent_key(k)) { + match self.table.find(hash, equivalent_key(k)) { Some((key, value)) => Some((key, value)), None => None, } @@ -1381,9 +1374,9 @@ where Q: Hash + Equivalent + ?Sized, { // Avoid `Option::map` because it bloats LLVM IR. - if !self.table.raw.is_empty() { + if !self.table.is_empty() { let hash = make_hash::(&self.hash_builder, k); - match self.table.raw.get_mut(hash, equivalent_key(k)) { + match self.table.find_mut(hash, equivalent_key(k)) { Some(&mut (ref key, ref mut value)) => Some((key, value)), None => None, } @@ -1416,9 +1409,9 @@ where where Q: Hash + Equivalent + ?Sized, { - if !self.table.raw.is_empty() { + if !self.table.is_empty() { let hash = make_hash::(&self.hash_builder, k); - self.table.raw.get(hash, equivalent_key(k)).is_some() + self.table.find(hash, equivalent_key(k)).is_some() } else { false } @@ -1453,9 +1446,9 @@ where Q: Hash + Equivalent + ?Sized, { // Avoid `Option::map` because it bloats LLVM IR. - if !self.table.raw.is_empty() { + if !self.table.is_empty() { let hash = make_hash::(&self.hash_builder, k); - match self.table.raw.get_mut(hash, equivalent_key(k)) { + match self.table.find_mut(hash, equivalent_key(k)) { Some(&mut (_, ref mut v)) => Some(v), None => None, } @@ -2056,7 +2049,7 @@ where /// primarily used for memory profiling. #[inline] pub fn allocation_size(&self) -> usize { - self.table.raw.allocation_size() + self.table.allocation_size() } } From 9603b50ee152f19cb7bb4661675080a9702253b3 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sat, 29 Nov 2025 17:09:46 -0500 Subject: [PATCH 3/5] Use table iterators --- src/map.rs | 73 ++++++++++++++++++++++-------------------------------- 1 file changed, 29 insertions(+), 44 deletions(-) diff --git a/src/map.rs b/src/map.rs index a4d324fb8..ff6f5ff0c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,5 +1,5 @@ -use crate::raw::{Allocator, Bucket, Global, RawDrain, RawExtractIf, RawIntoIter, RawIter}; -use crate::{DefaultHashBuilder, Equivalent, HashTable, TryReserveError}; +use crate::raw::{Allocator, Bucket, Global, RawExtractIf}; +use crate::{table, DefaultHashBuilder, Equivalent, HashTable, TryReserveError}; use ::alloc::borrow::ToOwned; use core::borrow::Borrow; use core::fmt::{self, Debug}; @@ -751,12 +751,8 @@ impl HashMap { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn iter(&self) -> Iter<'_, K, V> { - // Here we tie the lifetime of self to the iter. - unsafe { - Iter { - inner: self.table.raw.iter(), - marker: PhantomData, - } + Iter { + inner: self.table.iter(), } } @@ -796,12 +792,9 @@ impl HashMap { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn iter_mut(&mut self) -> IterMut<'_, K, V> { - // Here we tie the lifetime of self to the iter. - unsafe { - IterMut { - inner: self.table.raw.iter(), - marker: PhantomData, - } + IterMut { + inner: self.table.unsafe_iter(), + marker: PhantomData, } } @@ -886,7 +879,7 @@ impl HashMap { #[cfg_attr(feature = "inline-more", inline)] pub fn drain(&mut self) -> Drain<'_, K, V, A> { Drain { - inner: self.table.raw.drain(), + inner: self.table.drain(), } } @@ -2197,8 +2190,7 @@ where /// assert_eq!(iter.next(), None); /// ``` pub struct Iter<'a, K, V> { - inner: RawIter<(K, V)>, - marker: PhantomData<(&'a K, &'a V)>, + inner: table::Iter<'a, (K, V)>, } // FIXME(#26925) Remove in favor of `#[derive(Clone)]` @@ -2207,7 +2199,6 @@ impl Clone for Iter<'_, K, V> { fn clone(&self) -> Self { Iter { inner: self.inner.clone(), - marker: PhantomData, } } } @@ -2246,7 +2237,7 @@ impl fmt::Debug for Iter<'_, K, V> { /// assert_eq!(map.get(&2).unwrap(), &"Two Mississippi".to_owned()); /// ``` pub struct IterMut<'a, K, V> { - inner: RawIter<(K, V)>, + inner: table::UnsafeIter<'a, (K, V)>, // To ensure invariance with respect to V marker: PhantomData<(&'a K, &'a mut V)>, } @@ -2261,8 +2252,7 @@ impl IterMut<'_, K, V> { #[cfg_attr(feature = "inline-more", inline)] pub(super) fn iter(&self) -> Iter<'_, K, V> { Iter { - inner: self.inner.clone(), - marker: PhantomData, + inner: self.inner.iter(), } } } @@ -2298,7 +2288,7 @@ impl IterMut<'_, K, V> { /// assert_eq!(iter.next(), None); /// ``` pub struct IntoIter { - inner: RawIntoIter<(K, V), A>, + inner: table::IntoIter<(K, V), A>, } impl IntoIter { @@ -2307,7 +2297,6 @@ impl IntoIter { pub(super) fn iter(&self) -> Iter<'_, K, V> { Iter { inner: self.inner.iter(), - marker: PhantomData, } } } @@ -2594,7 +2583,7 @@ impl fmt::Debug for Values<'_, K, V> { /// assert_eq!(drain_iter.next(), None); /// ``` pub struct Drain<'a, K, V, A: Allocator = Global> { - inner: RawDrain<'a, (K, V), A>, + inner: table::Drain<'a, (K, V), A>, } impl Drain<'_, K, V, A> { @@ -2603,7 +2592,6 @@ impl Drain<'_, K, V, A> { pub(super) fn iter(&self) -> Iter<'_, K, V> { Iter { inner: self.inner.iter(), - marker: PhantomData, } } } @@ -3189,7 +3177,7 @@ impl IntoIterator for HashMap { #[cfg_attr(feature = "inline-more", inline)] fn into_iter(self) -> IntoIter { IntoIter { - inner: self.table.raw.into_iter(), + inner: self.table.into_iter(), } } } @@ -3199,21 +3187,17 @@ impl Default for Iter<'_, K, V> { fn default() -> Self { Self { inner: Default::default(), - marker: PhantomData, } } } -impl<'a, K, V> Iterator for Iter<'a, K, V> { +impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { type Item = (&'a K, &'a V); #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option<(&'a K, &'a V)> { // Avoid `Option::map` because it bloats LLVM IR. match self.inner.next() { - Some(x) => unsafe { - let r = x.as_ref(); - Some((&r.0, &r.1)) - }, + Some(x) => Some((&x.0, &x.1)), None => None, } } @@ -3227,20 +3211,17 @@ impl<'a, K, V> Iterator for Iter<'a, K, V> { Self: Sized, F: FnMut(B, Self::Item) -> B, { - self.inner.fold(init, |acc, x| unsafe { - let (k, v) = x.as_ref(); - f(acc, (k, v)) - }) + self.inner.fold(init, |acc, (k, v)| f(acc, (k, v))) } } -impl ExactSizeIterator for Iter<'_, K, V> { +impl<'a, K: 'a, V: 'a> ExactSizeIterator for Iter<'a, K, V> { #[cfg_attr(feature = "inline-more", inline)] fn len(&self) -> usize { self.inner.len() } } -impl FusedIterator for Iter<'_, K, V> {} +impl<'a, K: 'a, V: 'a> FusedIterator for Iter<'a, K, V> {} impl Default for IterMut<'_, K, V> { #[cfg_attr(feature = "inline-more", inline)] @@ -3258,10 +3239,12 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { fn next(&mut self) -> Option<(&'a K, &'a mut V)> { // Avoid `Option::map` because it bloats LLVM IR. match self.inner.next() { - Some(x) => unsafe { - let r = x.as_mut(); - Some((&r.0, &mut r.1)) - }, + Some(mut x) => { + // SAFETY: We match the lifetime of the original iterator + // with the correct variance provided by PhantomData. + let (ref k, ref mut v) = unsafe { x.as_mut() }; + Some((k, v)) + } None => None, } } @@ -3275,8 +3258,10 @@ impl<'a, K, V> Iterator for IterMut<'a, K, V> { Self: Sized, F: FnMut(B, Self::Item) -> B, { - self.inner.fold(init, |acc, x| unsafe { - let (k, v) = x.as_mut(); + self.inner.fold(init, |acc, mut x| { + // SAFETY: We match the lifetime of the original iterator + // with the correct variance provided by PhantomData. + let (ref k, ref mut v) = unsafe { x.as_mut() }; f(acc, (k, v)) }) } From 664c364fc4e82326e2f71d78e09d4784fbb44f00 Mon Sep 17 00:00:00 2001 From: ltdk Date: Tue, 2 Dec 2025 16:07:37 -0500 Subject: [PATCH 4/5] Use table entries --- src/map.rs | 168 ++++++++++++++++++----------------------------- src/raw/mod.rs | 8 --- src/raw_entry.rs | 22 ++++--- 3 files changed, 77 insertions(+), 121 deletions(-) diff --git a/src/map.rs b/src/map.rs index ff6f5ff0c..45f451c96 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1213,18 +1213,17 @@ where #[cfg_attr(feature = "inline-more", inline)] pub fn entry(&mut self, key: K) -> Entry<'_, K, V, S, A> { let hash = make_hash::(&self.hash_builder, &key); - if let Some(elem) = self.table.raw.find(hash, equivalent_key(&key)) { - Entry::Occupied(OccupiedEntry { - hash, - elem, - table: self, - }) - } else { - Entry::Vacant(VacantEntry { - hash, + let hasher = make_hasher(&self.hash_builder); + match self.table.entry(hash, equivalent_key(&key), hasher) { + table::Entry::Occupied(inner) => Entry::Occupied(OccupiedEntry { + inner, + marker: PhantomData, + }), + table::Entry::Vacant(inner) => Entry::Vacant(VacantEntry { + inner, key, - table: self, - }) + marker: PhantomData, + }), } } @@ -1251,18 +1250,17 @@ where Q: Hash + Equivalent + ?Sized, { let hash = make_hash::(&self.hash_builder, key); - if let Some(elem) = self.table.raw.find(hash, equivalent_key(key)) { - EntryRef::Occupied(OccupiedEntry { - hash, - elem, - table: self, - }) - } else { - EntryRef::Vacant(VacantEntryRef { - hash, + let hasher = make_hasher(&self.hash_builder); + match self.table.entry(hash, equivalent_key(key), hasher) { + table::Entry::Occupied(inner) => EntryRef::Occupied(OccupiedEntry { + inner, + marker: PhantomData, + }), + table::Entry::Vacant(inner) => EntryRef::Vacant(VacantEntryRef { + inner, key, - table: self, - }) + marker: PhantomData, + }), } } @@ -1824,13 +1822,10 @@ where /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn insert(&mut self, k: K, v: V) -> Option { - let hash = make_hash::(&self.hash_builder, &k); - match self.find_or_find_insert_index(hash, &k) { - Ok(bucket) => Some(mem::replace(unsafe { &mut bucket.as_mut().1 }, v)), - Err(index) => { - unsafe { - self.table.raw.insert_at_index(hash, index, (k, v)); - } + match self.entry(k) { + Entry::Occupied(mut entry) => Some(entry.insert(v)), + Entry::Vacant(entry) => { + entry.insert(v); None } } @@ -1912,11 +1907,10 @@ where #[cfg_attr(feature = "inline-more", inline)] pub unsafe fn insert_unique_unchecked(&mut self, k: K, v: V) -> (&K, &mut V) { let hash = make_hash::(&self.hash_builder, &k); - let bucket = + let entry = self.table - .raw - .insert(hash, (k, v), make_hasher::<_, V, S>(&self.hash_builder)); - let (k_ref, v_ref) = unsafe { bucket.as_mut() }; + .insert_unique(hash, (k, v), make_hasher::<_, V, S>(&self.hash_builder)); + let (k_ref, v_ref) = entry.into_mut(); (k_ref, v_ref) } @@ -2810,9 +2804,8 @@ impl Debug for Entry<'_, K, V, S, A> { /// assert_eq!(map.len(), 2); /// ``` pub struct OccupiedEntry<'a, K, V, S = DefaultHashBuilder, A: Allocator = Global> { - hash: u64, - elem: Bucket<(K, V)>, - table: &'a mut HashMap, + inner: table::OccupiedEntry<'a, (K, V), A>, + marker: PhantomData<&'a mut S>, } unsafe impl Send for OccupiedEntry<'_, K, V, S, A> @@ -2872,9 +2865,9 @@ impl Debug for OccupiedEntry<'_, K, V, S, A /// assert!(map[&"b"] == 20 && map.len() == 2); /// ``` pub struct VacantEntry<'a, K, V, S = DefaultHashBuilder, A: Allocator = Global> { - hash: u64, + inner: table::VacantEntry<'a, (K, V), A>, key: K, - table: &'a mut HashMap, + marker: PhantomData<&'a mut S>, } impl Debug for VacantEntry<'_, K, V, S, A> { @@ -3014,9 +3007,9 @@ where /// assert!(map["b"] == 20 && map.len() == 2); /// ``` pub struct VacantEntryRef<'map, 'key, K, Q: ?Sized, V, S, A: Allocator = Global> { - hash: u64, + inner: table::VacantEntry<'map, (K, V), A>, key: &'key Q, - table: &'map mut HashMap, + marker: PhantomData<&'map mut S>, } impl Debug for VacantEntryRef<'_, '_, K, Q, V, S, A> @@ -3823,7 +3816,7 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn key(&self) -> &K { - unsafe { &self.elem.as_ref().0 } + &self.inner.get().0 } /// Take the ownership of the key and value from the map. @@ -3852,7 +3845,7 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn remove_entry(self) -> (K, V) { - unsafe { self.table.table.raw.remove(self.elem).0 } + self.inner.remove().0 } /// Gets a reference to the value in the entry. @@ -3873,7 +3866,7 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn get(&self) -> &V { - unsafe { &self.elem.as_ref().1 } + &self.inner.get().1 } /// Gets a mutable reference to the value in the entry. @@ -3905,7 +3898,7 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn get_mut(&mut self) -> &mut V { - unsafe { &mut self.elem.as_mut().1 } + &mut self.inner.get_mut().1 } /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry @@ -3936,7 +3929,7 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn into_mut(self) -> &'a mut V { - unsafe { &mut self.elem.as_mut().1 } + &mut self.inner.into_mut().1 } /// Converts the `OccupiedEntry` into a reference to the key and a @@ -3971,7 +3964,7 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn into_entry(self) -> (&'a K, &'a mut V) { - let (key, val) = unsafe { self.elem.as_mut() }; + let (key, val) = self.inner.into_mut(); (key, val) } @@ -4078,30 +4071,25 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { where F: FnOnce(&K, V) -> Option, { - unsafe { - let mut spare_key = None; - - self.table - .table - .raw - .replace_bucket_with(self.elem.clone(), |(key, value)| { - if let Some(new_value) = f(&key, value) { - Some((key, new_value)) - } else { - spare_key = Some(key); - None - } - }); + let mut spare_key = None; - if let Some(key) = spare_key { - Entry::Vacant(VacantEntry { - hash: self.hash, - key, - table: self.table, - }) + match self.inner.replace_entry_with(|(key, value)| { + if let Some(new_value) = f(&key, value) { + Some((key, new_value)) } else { - Entry::Occupied(self) + spare_key = Some(key); + None } + }) { + table::Entry::Vacant(inner) => Entry::Vacant(VacantEntry { + inner, + key: unsafe { spare_key.unwrap_unchecked() }, + marker: PhantomData, + }), + table::Entry::Occupied(inner) => Entry::Occupied(OccupiedEntry { + inner, + marker: PhantomData, + }), } } } @@ -4164,13 +4152,7 @@ impl<'a, K, V, S, A: Allocator> VacantEntry<'a, K, V, S, A> { K: Hash, S: BuildHasher, { - let table = &mut self.table.table.raw; - let entry = table.insert_entry( - self.hash, - (self.key, value), - make_hasher::<_, V, S>(&self.table.hash_builder), - ); - &mut entry.1 + &mut self.inner.insert((self.key, value)).into_mut().1 } /// Sets the value of the entry with the [`VacantEntry`]'s key, @@ -4195,15 +4177,9 @@ impl<'a, K, V, S, A: Allocator> VacantEntry<'a, K, V, S, A> { K: Hash, S: BuildHasher, { - let elem = self.table.table.raw.insert( - self.hash, - (self.key, value), - make_hasher::<_, V, S>(&self.table.hash_builder), - ); OccupiedEntry { - hash: self.hash, - elem, - table: self.table, + inner: self.inner.insert((self.key, value)), + marker: PhantomData, } } } @@ -4504,13 +4480,7 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, Q: ToOwned, S: BuildHasher, { - let table = &mut self.table.table.raw; - let entry = table.insert_entry( - self.hash, - (self.key.to_owned(), value), - make_hasher::<_, V, S>(&self.table.hash_builder), - ); - &mut entry.1 + &mut self.inner.insert((self.key.to_owned(), value)).into_mut().1 } /// Sets the key and value of the entry and returns a mutable reference to @@ -4575,15 +4545,9 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, Q: ToOwned, S: BuildHasher, { - let elem = self.table.table.raw.insert( - self.hash, - (self.key.to_owned(), value), - make_hasher::<_, V, S>(&self.table.hash_builder), - ); OccupiedEntry { - hash: self.hash, - elem, - table: self.table, + inner: self.inner.insert((self.key.to_owned(), value)), + marker: PhantomData, } } @@ -4626,15 +4590,9 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, (self.key).equivalent(&key), "key used for Entry creation is not equivalent to the one used for insertion" ); - let elem = self.table.table.raw.insert( - self.hash, - (key, value), - make_hasher::<_, V, S>(&self.table.hash_builder), - ); OccupiedEntry { - hash: self.hash, - elem, - table: self.table, + inner: self.inner.insert((key, value)), + marker: PhantomData, } } } diff --git a/src/raw/mod.rs b/src/raw/mod.rs index 235b6141e..0e1664c7f 100644 --- a/src/raw/mod.rs +++ b/src/raw/mod.rs @@ -1096,14 +1096,6 @@ impl RawTable { } } - /// Inserts a new element into the table, and returns a mutable reference to it. - /// - /// This does not check if the given element already exists in the table. - #[cfg_attr(feature = "inline-more", inline)] - pub fn insert_entry(&mut self, hash: u64, value: T, hasher: impl Fn(&T) -> u64) -> &mut T { - unsafe { self.insert(hash, value, hasher).as_mut() } - } - /// Inserts a new element into the table, without growing the table. /// /// There must be enough space in the table to insert the new element. diff --git a/src/raw_entry.rs b/src/raw_entry.rs index 69fe08962..72d2de6e6 100644 --- a/src/raw_entry.rs +++ b/src/raw_entry.rs @@ -1365,11 +1365,15 @@ impl<'a, K, V, S, A: Allocator> RawVacantEntryMut<'a, K, V, S, A> { K: Hash, S: BuildHasher, { - let &mut (ref mut k, ref mut v) = self.table.insert_entry( - hash, - (key, value), - make_hasher::<_, V, S>(self.hash_builder), - ); + let &mut (ref mut k, ref mut v) = unsafe { + self.table + .insert( + hash, + (key, value), + make_hasher::<_, V, S>(self.hash_builder), + ) + .as_mut() + }; (k, v) } @@ -1420,9 +1424,11 @@ impl<'a, K, V, S, A: Allocator> RawVacantEntryMut<'a, K, V, S, A> { where H: Fn(&K) -> u64, { - let &mut (ref mut k, ref mut v) = self - .table - .insert_entry(hash, (key, value), |x| hasher(&x.0)); + let &mut (ref mut k, ref mut v) = unsafe { + self.table + .insert(hash, (key, value), |x| hasher(&x.0)) + .as_mut() + }; (k, v) } From 339b15f2b77f80fe1af9beddfbe3672128b16408 Mon Sep 17 00:00:00 2001 From: ltdk Date: Sun, 21 Dec 2025 19:33:26 -0500 Subject: [PATCH 5/5] extract_if with traits --- src/map.rs | 42 ++++++++++++++++++++++++++++++------------ src/set.rs | 46 +++++++++++++++++++++++++++++++--------------- src/table.rs | 23 +++++++++++++++++------ 3 files changed, 78 insertions(+), 33 deletions(-) diff --git a/src/map.rs b/src/map.rs index 45f451c96..07504166e 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1,4 +1,4 @@ -use crate::raw::{Allocator, Bucket, Global, RawExtractIf}; +use crate::raw::{Allocator, Bucket, Global}; use crate::{table, DefaultHashBuilder, Equivalent, HashTable, TryReserveError}; use ::alloc::borrow::ToOwned; use core::borrow::Borrow; @@ -960,14 +960,10 @@ impl HashMap { #[cfg_attr(feature = "inline-more", inline)] pub fn extract_if(&mut self, f: F) -> ExtractIf<'_, K, V, F, A> where - F: FnMut(&K, &mut V) -> bool, + F: Filter, { ExtractIf { - f, - inner: RawExtractIf { - iter: unsafe { self.table.raw.iter() }, - table: &mut self.table.raw, - }, + inner: self.table.extract_if(KeyVal(f)), } } @@ -2590,6 +2586,29 @@ impl Drain<'_, K, V, A> { } } +/// Adapter between [`map::Filter`](Filter) and [`table::Filter`]. +struct KeyVal(F); +impl> table::Filter<(K, V)> for KeyVal { + #[inline] + fn should_extract(&mut self, (ref key, ref mut val): &mut (K, V)) -> bool { + self.0.should_extract(key, val) + } +} + +/// Filter for [`ExtractIf`]. +/// +/// Accepts `FnMut(&K, &mut V) -> bool`, but can be implemented directly. +pub trait Filter { + /// Whether the element should be extracted. + fn should_extract(&mut self, key: &K, value: &mut V) -> bool; +} +impl bool> Filter for F { + #[inline] + fn should_extract(&mut self, key: &K, value: &mut V) -> bool { + (self)(key, value) + } +} + /// A draining iterator over entries of a `HashMap` which don't satisfy the predicate /// `f(&k, &mut v)` in arbitrary order. The iterator element type is `(K, V)`. /// @@ -2623,25 +2642,24 @@ 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: table::ExtractIf<'a, (K, V), KeyVal, A>, } impl Iterator for ExtractIf<'_, K, V, F, A> where - F: FnMut(&K, &mut V) -> bool, + F: Filter, A: Allocator, { type Item = (K, V); #[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() } #[inline] fn size_hint(&self) -> (usize, Option) { - (0, self.inner.iter.size_hint().1) + self.inner.size_hint() } } diff --git a/src/set.rs b/src/set.rs index 5718231b6..c00a12d7c 100644 --- a/src/set.rs +++ b/src/set.rs @@ -5,8 +5,8 @@ use core::ops::{BitAnd, BitAndAssign, BitOr, BitOrAssign, BitXor, BitXorAssign, use core::{fmt, mem}; use map::make_hash; -use super::map::{self, HashMap, Keys}; -use crate::raw::{Allocator, Global, RawExtractIf}; +use crate::map::{self, HashMap, Keys}; +use crate::raw::{Allocator, Global}; use crate::DefaultHashBuilder; // Future Optimization (FIXME!) @@ -403,14 +403,10 @@ impl HashSet { #[cfg_attr(feature = "inline-more", inline)] pub fn extract_if(&mut self, f: F) -> ExtractIf<'_, T, F, A> where - F: FnMut(&T) -> bool, + F: Filter, { ExtractIf { - f, - inner: RawExtractIf { - iter: unsafe { self.map.table.raw.iter() }, - table: &mut self.map.table.raw, - }, + inner: self.map.extract_if(Key(f)), } } @@ -1663,6 +1659,29 @@ pub struct Drain<'a, K, A: Allocator = Global> { iter: map::Drain<'a, K, (), A>, } +/// Adapter between [`set::Filter`](Filter) and [`map::Filter`]. +struct Key(F); +impl> map::Filter for Key { + #[inline] + fn should_extract(&mut self, key: &T, (): &mut ()) -> bool { + self.0.should_extract(key) + } +} + +/// Filter for [`ExtractIf`]. +/// +/// Accepts `FnMut(&K) -> bool`, but can be implemented directly. +pub trait Filter { + /// Whether the element should be extracted. + fn should_extract(&mut self, key: &K) -> bool; +} +impl bool> Filter for F { + #[inline] + fn should_extract(&mut self, key: &K) -> bool { + (self)(key) + } +} + /// A draining iterator over entries of a `HashSet` which don't satisfy the predicate `f`. /// /// This `struct` is created by the [`extract_if`] method on [`HashSet`]. See its @@ -1672,8 +1691,7 @@ pub struct Drain<'a, K, A: Allocator = Global> { /// [`HashSet`]: struct.HashSet.html #[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::ExtractIf<'a, K, (), Key, A>, } /// A lazy iterator producing elements in the intersection of `HashSet`s. @@ -1906,20 +1924,18 @@ impl fmt::Debug for Drain<'_, K, A> { impl Iterator for ExtractIf<'_, K, F, A> where - F: FnMut(&K) -> bool, + F: Filter, { type Item = K; #[cfg_attr(feature = "inline-more", inline)] fn next(&mut self) -> Option { - self.inner - .next(|&mut (ref k, ())| (self.f)(k)) - .map(|(k, ())| k) + self.inner.next().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 6b28b7902..e59afe7c0 100644 --- a/src/table.rs +++ b/src/table.rs @@ -1381,7 +1381,7 @@ where /// ``` pub fn extract_if(&mut self, f: F) -> ExtractIf<'_, T, F, A> where - F: FnMut(&mut T) -> bool, + F: Filter, { ExtractIf { f, @@ -3157,6 +3157,20 @@ impl fmt::Debug for Drain<'_, T, A> { } } +/// Filter for [`ExtractIf`]. +/// +/// Accepts `FnMut(&mut T) -> bool`, but can be implemented directly. +pub trait Filter { + /// Whether the element should be extracted. + fn should_extract(&mut self, elem: &mut T) -> bool; +} +impl bool> Filter for F { + #[inline] + fn should_extract(&mut self, elem: &mut T) -> bool { + (self)(elem) + } +} + /// A draining iterator over entries of a `HashTable` which don't satisfy the predicate `f`. /// /// This `struct` is created by [`HashTable::extract_if`]. See its @@ -3167,15 +3181,12 @@ pub struct ExtractIf<'a, T, F, A: Allocator = Global> { inner: RawExtractIf<'a, T, A>, } -impl Iterator for ExtractIf<'_, T, F, A> -where - F: FnMut(&mut T) -> bool, -{ +impl, A: Allocator> Iterator for ExtractIf<'_, T, F, A> { type Item = T; #[inline] fn next(&mut self) -> Option { - self.inner.next(|val| (self.f)(val)) + self.inner.next(|val| self.f.should_extract(val)) } #[inline]