Skip to content

Commit 4d7f1f9

Browse files
committed
Add hash_table::OccupiedEntry::replace_entry_with to mirror HashMap API
1 parent bba4a01 commit 4d7f1f9

File tree

3 files changed

+82
-12
lines changed

3 files changed

+82
-12
lines changed

src/raw/mod.rs

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,11 +1127,12 @@ impl<T, A: Allocator> RawTable<T, A> {
11271127
/// Temporary 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 `true` if the bucket still contains an element. Always returns the
1131+
/// associated bucket index and tag.
11311132
///
11321133
/// This does not check if the given bucket is actually occupied.
11331134
#[cfg_attr(feature = "inline-more", inline)]
1134-
pub unsafe fn replace_bucket_with<F>(&mut self, bucket: Bucket<T>, f: F) -> bool
1135+
pub unsafe fn replace_bucket_with<F>(&mut self, bucket: Bucket<T>, f: F) -> (bool, usize, Tag)
11351136
where
11361137
F: FnOnce(T) -> Option<T>,
11371138
{
@@ -1140,15 +1141,19 @@ impl<T, A: Allocator> RawTable<T, A> {
11401141
debug_assert!(self.is_bucket_full(index));
11411142
let old_growth_left = self.table.growth_left;
11421143
let item = self.remove(bucket).0;
1143-
if let Some(new_item) = f(item) {
1144-
self.table.growth_left = old_growth_left;
1145-
self.table.set_ctrl(index, old_ctrl);
1146-
self.table.items += 1;
1147-
self.bucket(index).write(new_item);
1148-
true
1149-
} else {
1150-
false
1151-
}
1144+
(
1145+
if let Some(new_item) = f(item) {
1146+
self.table.growth_left = old_growth_left;
1147+
self.table.set_ctrl(index, old_ctrl);
1148+
self.table.items += 1;
1149+
self.bucket(index).write(new_item);
1150+
true
1151+
} else {
1152+
false
1153+
},
1154+
index,
1155+
old_ctrl,
1156+
)
11521157
}
11531158

11541159
/// Searches for an element in the table. If the element is not found,

src/raw_entry.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,7 @@ 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 (still_occupied, _, _) = self
12861286
.table
12871287
.replace_bucket_with(self.elem.clone(), |(key, value)| {
12881288
f(&key, value).map(|new_value| (key, new_value))

src/table.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2246,6 +2246,71 @@ where
22462246
pub fn bucket_index(&self) -> usize {
22472247
unsafe { self.table.raw.bucket_index(&self.bucket) }
22482248
}
2249+
2250+
/// Provides owned access to the value of the entry and allows to replace or
2251+
/// remove it based on the value of the returned option.
2252+
///
2253+
/// # Examples
2254+
///
2255+
/// ```
2256+
/// use hashbrown::{HashTable, DefaultHashBuilder};
2257+
/// use hashbrown::hash_table::Entry;
2258+
/// use std::hash::BuildHasher;
2259+
///
2260+
/// let mut table = HashTable::new();
2261+
/// let hasher = DefaultHashBuilder::default();
2262+
/// let hasher = |val: &_| hasher.hash_one(val);
2263+
/// table.insert_unique(42);
2264+
///
2265+
/// let entry = match table.entry("poneyland") {
2266+
/// Entry::Occupied(e) => {
2267+
/// e.replace_entry_with(|v| {
2268+
/// assert_eq!(v, 42);
2269+
/// Some(v + 1)
2270+
/// })
2271+
/// }
2272+
/// Entry::Vacant(_) => panic!(),
2273+
/// };
2274+
///
2275+
/// match entry {
2276+
/// Entry::Occupied(e) => {
2277+
/// assert_eq!(e.get(), &43);
2278+
/// }
2279+
/// Entry::Vacant(_) => panic!(),
2280+
/// }
2281+
///
2282+
/// assert!(!table.is_empty());
2283+
///
2284+
/// let entry = match table.entry(43) {
2285+
/// Entry::Occupied(e) => e.replace_entry_with(|_v| None),
2286+
/// Entry::Vacant(_) => panic!(),
2287+
/// };
2288+
///
2289+
/// match entry {
2290+
/// Entry::Vacant(e) => {
2291+
/// // nice!
2292+
/// }
2293+
/// Entry::Occupied(_) => panic!(),
2294+
/// }
2295+
///
2296+
/// assert!(table.is_empty());
2297+
/// ```
2298+
#[cfg_attr(feature = "inline-more", inline)]
2299+
pub fn replace_entry_with<F>(self, f: F) -> Entry<'a, T, A>
2300+
where
2301+
F: FnOnce(T) -> Option<T>,
2302+
{
2303+
unsafe {
2304+
match self.table.raw.replace_bucket_with(self.bucket.clone(), f) {
2305+
(true, _, _) => Entry::Occupied(self),
2306+
(false, index, tag) => Entry::Vacant(VacantEntry {
2307+
tag,
2308+
index,
2309+
table: self.table,
2310+
}),
2311+
}
2312+
}
2313+
}
22492314
}
22502315

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

0 commit comments

Comments
 (0)