Skip to content

Commit

Permalink
HashMap: #or_default_entry and #or_insert_entry
Browse files Browse the repository at this point in the history
I often find myself in a situation where I would like to insert a key
into a map via `#entry_ref` and then use the key from the resulting
entry.

```
// Get a byte slice from a buffer
let key = client.request.get(index);

// Insert the value (default)
let mut entry = match queues.entry_ref(&key) {
    EntryRef::Occupied(entry) => entry,
    EntryRef::Vacant(entry) => entry.insert_entry(Default::default()),
};

// Use the value
entry.get_mut().insert_back(client.id);

// Reuse the key instead of copying the bytes again
keys.insert(client.id, entry.key());
```

This is common enough that I'd love to have functions for it, similar to
`insert_entry`.

```
// Get a byte slice from a buffer
let key = client.request.get(index);

// Insert the value (default)
let mut entry = queues.entry_ref(&key).or_default_entry();

// Use the value
entry.get_mut().insert_back(client.id);

// Reuse the key instead of copying the bytes again
keys.insert(client.id, entry.key());
```
  • Loading branch information
braddunbar committed Dec 17, 2024
1 parent 25365fc commit 5b9c757
Showing 1 changed file with 65 additions and 0 deletions.
65 changes: 65 additions & 0 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3527,6 +3527,38 @@ impl<'a, K, V, S, A: Allocator> Entry<'a, K, V, S, A> {
}
}

/// Ensures a value is in the entry by inserting the default if empty,
/// and returns an [`OccupiedEntry`].
///
/// # Examples
///
/// ```
/// use hashbrown::HashMap;
///
/// let mut map: HashMap<&str, u32> = HashMap::new();
///
/// // nonexistent key
/// let entry = map.entry("poneyland").or_insert_entry(3);
/// assert_eq!(entry.key(), &"poneyland");
/// assert_eq!(entry.get(), &3);
///
/// // existing key
/// let mut entry = map.entry("poneyland").or_insert_entry(10);
/// assert_eq!(entry.key(), &"poneyland");
/// assert_eq!(entry.get(), &3);
/// ```
#[cfg_attr(feature = "inline-more", inline)]
pub fn or_insert_entry(self, default: V) -> OccupiedEntry<'a, K, V, S, A>
where
K: Hash,
S: BuildHasher,
{
match self {
Entry::Occupied(entry) => entry,
Entry::Vacant(entry) => entry.insert_entry(default),
}
}

/// Ensures a value is in the entry by inserting the result of the default function if empty,
/// and returns a mutable reference to the value in the entry.
///
Expand Down Expand Up @@ -4328,6 +4360,39 @@ impl<'a, 'b, K, Q: ?Sized, V: Default, S, A: Allocator> EntryRef<'a, 'b, K, Q, V
EntryRef::Vacant(entry) => entry.insert(Default::default()),
}
}

/// Ensures a value is in the entry by inserting the default value if empty,
/// and returns an [`OccupiedEntry`].
///
/// # Examples
///
/// ```
/// use hashbrown::HashMap;
///
/// let mut map: HashMap<String, Option<u32>> = HashMap::new();
///
/// // nonexistent key
/// let entry = map.entry_ref("poneyland").or_default_entry();
/// assert_eq!(entry.key(), &"poneyland");
/// assert_eq!(entry.get(), &None);
///
/// // existing key
/// map.insert("horseland".to_string(), Some(3));
/// let entry = map.entry_ref("horseland").or_default_entry();
/// assert_eq!(entry.key(), &"horseland");
/// assert_eq!(entry.get(), &Some(3));
/// ```
#[cfg_attr(feature = "inline-more", inline)]
pub fn or_default_entry(self) -> OccupiedEntry<'a, K, V, S, A>
where
K: Hash + From<&'b Q>,
S: BuildHasher,
{
match self {
EntryRef::Occupied(entry) => entry,
EntryRef::Vacant(entry) => entry.insert_entry(Default::default()),
}
}
}

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

0 comments on commit 5b9c757

Please sign in to comment.