Skip to content

Commit

Permalink
Add immutable map accessors to OccupiedEntry and VacantEntry
Browse files Browse the repository at this point in the history
Without these, it is not possible to inspect other keys in the map while
deciding how to process the entry, forcing the user to either perform
unnecessary checks before calling entry or performing multiple map
lookups for the entry key.

For example:

```rust
fn some_expensive_operation(...) -> bool { ... }

// old way, with two lookups for `key`
if !map.contains_key(key) && some_expensive_operation(map.get(other_key)) {
    map.insert(key, val);
}

// old way, with expensive operation done unnecessarily when key is
// already present
if some_expensive_operation(map.get(other_key)) {
    if let VacantEntry(e) = map.entry(key) {
        e.insert(val);
    }
}

// new way, with one lookup for `key`
if let VacantEntry(e) = map.entry(key) {
  if some_expensive_operation(e.map().get(other_key)) {
    e.insert(val);
  }
}
```

We do not provide accessors returning a mutable reference to the map
because doing so would make it possible to invalidate the entry itself.
  • Loading branch information
apasel422 committed Dec 11, 2024
1 parent 25365fc commit dc60c71
Showing 1 changed file with 48 additions and 0 deletions.
48 changes: 48 additions & 0 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4011,6 +4011,30 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> {
}
}
}

/// Gets a reference to the map that owns this entry.
///
/// This can be used to perform additional lookups while deciding
/// how to process the entry.
///
/// # Examples
///
/// ```
/// use hashbrown::HashMap;
/// use hashbrown::hash_map::Entry;
///
/// let mut map: HashMap<&str, u32> = HashMap::new();
///
/// fn some_expensive_operation(val: Option<&u32>) -> bool { true }
///
/// match map.entry("abc") {
/// Entry::Occupied(mut e) => if some_expensive_operation(e.map().get("def")) { e.insert(5); },
/// Entry::Vacant(_) => {}
/// }
/// ```
pub fn map(&self) -> &HashMap<K, V, S, A> {
self.table
}
}

impl<'a, K, V, S, A: Allocator> VacantEntry<'a, K, V, S, A> {
Expand Down Expand Up @@ -4113,6 +4137,30 @@ impl<'a, K, V, S, A: Allocator> VacantEntry<'a, K, V, S, A> {
table: self.table,
}
}

/// Gets a reference to the map that owns this entry.
///
/// This can be used to perform additional lookups while deciding
/// how to process the entry.
///
/// # Examples
///
/// ```
/// use hashbrown::HashMap;
/// use hashbrown::hash_map::Entry;
///
/// let mut map: HashMap<&str, u32> = HashMap::new();
///
/// fn some_expensive_operation(val: Option<&u32>) -> bool { true }
///
/// match map.entry("abc") {
/// Entry::Vacant(mut e) => if some_expensive_operation(e.map().get("def")) { e.insert(5); },
/// Entry::Occupied(_) => {}
/// }
/// ```
pub fn map(&self) -> &HashMap<K, V, S, A> {
self.table
}
}

impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> EntryRef<'a, 'b, K, Q, V, S, A> {
Expand Down

0 comments on commit dc60c71

Please sign in to comment.