Skip to content

Commit 329cfc9

Browse files
committed
Add hash_table::OccupiedEntry::replace_entry_with to mirror HashMap API
1 parent 13f3a22 commit 329cfc9

File tree

3 files changed

+80
-7
lines changed

3 files changed

+80
-7
lines changed

src/raw/mod.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1124,14 +1124,14 @@ impl<T, A: Allocator> RawTable<T, A> {
11241124
bucket
11251125
}
11261126

1127-
/// Temporary removes a bucket, applying the given function to the removed
1127+
/// Temporarily removes a bucket, applying the given function to the removed
11281128
/// element and optionally put back the returned value in the same bucket.
11291129
///
1130-
/// Returns `true` if the bucket still contains an element
1130+
/// Returns tag for bucket if the bucket is emptied out.
11311131
///
11321132
/// This does not check if the given bucket is actually occupied.
11331133
#[cfg_attr(feature = "inline-more", inline)]
1134-
pub unsafe fn replace_bucket_with<F>(&mut self, bucket: Bucket<T>, f: F) -> bool
1134+
pub(crate) unsafe fn replace_bucket_with<F>(&mut self, bucket: Bucket<T>, f: F) -> Option<Tag>
11351135
where
11361136
F: FnOnce(T) -> Option<T>,
11371137
{
@@ -1145,9 +1145,9 @@ impl<T, A: Allocator> RawTable<T, A> {
11451145
self.table.set_ctrl(index, old_ctrl);
11461146
self.table.items += 1;
11471147
self.bucket(index).write(new_item);
1148-
true
1148+
None
11491149
} else {
1150-
false
1150+
Some(old_ctrl)
11511151
}
11521152
}
11531153

src/raw_entry.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,13 +1282,13 @@ impl<'a, K, V, S, A: Allocator> RawOccupiedEntryMut<'a, K, V, S, A> {
12821282
F: FnOnce(&K, V) -> Option<V>,
12831283
{
12841284
unsafe {
1285-
let still_occupied = self
1285+
let tag = self
12861286
.table
12871287
.replace_bucket_with(self.elem.clone(), |(key, value)| {
12881288
f(&key, value).map(|new_value| (key, new_value))
12891289
});
12901290

1291-
if still_occupied {
1291+
if tag.is_none() {
12921292
RawEntryMut::Occupied(self)
12931293
} else {
12941294
RawEntryMut::Vacant(RawVacantEntryMut {

src/table.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2266,6 +2266,79 @@ where
22662266
pub fn bucket_index(&self) -> usize {
22672267
unsafe { self.table.raw.bucket_index(&self.bucket) }
22682268
}
2269+
2270+
/// Provides owned access to the value of the entry and allows to replace or
2271+
/// remove it based on the value of the returned option.
2272+
///
2273+
/// The hash of the new item should be the same as the old item.
2274+
///
2275+
/// # Examples
2276+
///
2277+
/// ```
2278+
/// # #[cfg(feature = "nightly")]
2279+
/// # fn test() {
2280+
/// use hashbrown::{HashTable, DefaultHashBuilder};
2281+
/// use hashbrown::hash_table::Entry;
2282+
/// use std::hash::BuildHasher;
2283+
///
2284+
/// let mut table = HashTable::new();
2285+
/// let hasher = DefaultHashBuilder::default();
2286+
/// let hasher = |(ref key, _): &_| hasher.hash_one(key);
2287+
/// table.insert_unique(hasher(&("poneyland", 42)), ("poneyland", 42), hasher);
2288+
///
2289+
/// let entry = match table.entry(hasher(&("poneyland", 42)), |entry| entry.0 == "poneyland", hasher) {
2290+
/// Entry::Occupied(e) => unsafe {
2291+
/// e.replace_entry_with(|(k, v)| {
2292+
/// assert_eq!(k, "poneyland");
2293+
/// assert_eq!(v, 42);
2294+
/// Some(("poneyland", v + 1))
2295+
/// })
2296+
/// }
2297+
/// Entry::Vacant(_) => panic!(),
2298+
/// };
2299+
///
2300+
/// match entry {
2301+
/// Entry::Occupied(e) => {
2302+
/// assert_eq!(e.get(), &("poneyland", 43));
2303+
/// }
2304+
/// Entry::Vacant(_) => panic!(),
2305+
/// }
2306+
///
2307+
/// let entry = match table.entry(hasher(&("poneyland", 43)), |entry| entry.0 == "poneyland", hasher) {
2308+
/// Entry::Occupied(e) => unsafe { e.replace_entry_with(|(_k, _v)| None) },
2309+
/// Entry::Vacant(_) => panic!(),
2310+
/// };
2311+
///
2312+
/// match entry {
2313+
/// Entry::Vacant(e) => {
2314+
/// // nice!
2315+
/// }
2316+
/// Entry::Occupied(_) => panic!(),
2317+
/// }
2318+
///
2319+
/// assert!(table.is_empty());
2320+
/// # }
2321+
/// # fn main() {
2322+
/// # #[cfg(feature = "nightly")]
2323+
/// # test()
2324+
/// # }
2325+
/// ```
2326+
#[cfg_attr(feature = "inline-more", inline)]
2327+
pub fn replace_entry_with<F>(self, f: F) -> Entry<'a, T, A>
2328+
where
2329+
F: FnOnce(T) -> Option<T>,
2330+
{
2331+
unsafe {
2332+
match self.table.raw.replace_bucket_with(self.bucket.clone(), f) {
2333+
None => Entry::Occupied(self),
2334+
Some(tag) => Entry::Vacant(VacantEntry {
2335+
tag,
2336+
index: self.bucket_index(),
2337+
table: self.table,
2338+
}),
2339+
}
2340+
}
2341+
}
22692342
}
22702343

22712344
/// A view into a vacant entry in a `HashTable`.

0 commit comments

Comments
 (0)