From 6a6e760d0f34f03a9c0e1275d877828a2b14bd08 Mon Sep 17 00:00:00 2001 From: ltdk Date: Wed, 3 Dec 2025 09:19:48 -0500 Subject: [PATCH] Add hash_map::{OccupiedEntry::into_entry, VacantEntryRef::insert_entry_with_key} --- src/map.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++++++------ src/set.rs | 28 +++++++-------- 2 files changed, 100 insertions(+), 27 deletions(-) diff --git a/src/map.rs b/src/map.rs index 378bcb0bb..c6debd216 100644 --- a/src/map.rs +++ b/src/map.rs @@ -3963,6 +3963,42 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> { unsafe { &mut self.elem.as_mut().1 } } + /// Converts the `OccupiedEntry` into a reference to the key and a + /// mutable reference to the value in the entry with a lifetime bound to the + /// map itself. + /// + /// If you need multiple references to the `OccupiedEntry`, see [`key`] and + /// [`get_mut`]. + /// + /// [`key`]: Self::key + /// [`get_mut`]: Self::get_mut + /// + /// # Examples + /// + /// ``` + /// use hashbrown::hash_map::{Entry, HashMap}; + /// + /// let mut map: HashMap<&str, u32> = HashMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// + /// let key_val: (&&str, &mut u32); + /// match map.entry("poneyland") { + /// Entry::Occupied(entry) => key_val = entry.into_entry(), + /// Entry::Vacant(_) => panic!(), + /// } + /// *key_val.1 += 10; + /// + /// assert_eq!(key_val, (&"poneyland", &mut 22)); + /// assert_eq!(map["poneyland"], 22); + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn into_entry(self) -> (&'a K, &'a mut V) { + let (key, val) = unsafe { self.elem.as_mut() }; + (key, val) + } + /// Sets the value of the entry, and returns the entry's old value. /// /// # Examples @@ -4535,17 +4571,7 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, Q: Equivalent, S: BuildHasher, { - let table = &mut self.table.table; - assert!( - (self.key).equivalent(&key), - "key used for Entry creation is not equivalent to the one used for insertion" - ); - let entry = table.insert_entry( - self.hash, - (key, value), - make_hasher::<_, V, S>(&self.table.hash_builder), - ); - &mut entry.1 + self.insert_entry_with_key(key, value).into_mut() } /// Sets the value of the entry with the [`VacantEntryRef`]'s key, @@ -4582,6 +4608,57 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, table: self.table, } } + + /// Sets the key and value of the entry and returns an [`OccupiedEntry`]. + /// + /// Unlike [`VacantEntryRef::insert_entry`], this method allows the key to + /// be explicitly specified, which is useful for key types that don't + /// implement `K: From<&Q>`. + /// + /// # Panics + /// + /// This method panics if `key` is not equivalent to the key used to create + /// the `VacantEntryRef`. + /// + /// # Example + /// + /// ``` + /// use hashbrown::hash_map::EntryRef; + /// use hashbrown::HashMap; + /// + /// let mut map = HashMap::<(String, String), char>::new(); + /// let k = ("c".to_string(), "C".to_string()); + /// let r = match map.entry_ref(&k) { + /// // Insert cannot be used here because tuples do not implement From. + /// // However this works because we can manually clone instead. + /// EntryRef::Vacant(r) => r.insert_entry_with_key(k.clone(), 'c'), + /// // In this branch we avoid the clone. + /// EntryRef::Occupied(r) => r, + /// }; + /// assert_eq!(r.get(), &'c'); + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn insert_entry_with_key(self, key: K, value: V) -> OccupiedEntry<'map, K, V, S, A> + where + K: Hash, + Q: Equivalent, + S: BuildHasher, + { + assert!( + (self.key).equivalent(&key), + "key used for Entry creation is not equivalent to the one used for insertion" + ); + let elem = self.table.table.insert( + self.hash, + (key, value), + make_hasher::<_, V, S>(&self.table.hash_builder), + ); + OccupiedEntry { + hash: self.hash, + elem, + table: self.table, + } + } } impl FromIterator<(K, V)> for HashMap diff --git a/src/set.rs b/src/set.rs index a875d2f50..a73900925 100644 --- a/src/set.rs +++ b/src/set.rs @@ -912,12 +912,12 @@ where /// ``` #[cfg_attr(feature = "inline-more", inline)] pub fn get_or_insert(&mut self, value: T) -> &T { - let hash = make_hash(&self.map.hash_builder, &value); - let bucket = match self.map.find_or_find_insert_index(hash, &value) { - Ok(bucket) => bucket, - Err(index) => unsafe { self.map.table.insert_at_index(hash, index, (value, ())) }, - }; - unsafe { &bucket.as_ref().0 } + match self.map.entry(value) { + map::Entry::Occupied(entry) => entry, + map::Entry::Vacant(entry) => entry.insert_entry(()), + } + .into_entry() + .0 } /// Inserts a value computed from `f` into the set if the given `value` is @@ -951,16 +951,12 @@ where Q: Hash + Equivalent + ?Sized, F: FnOnce(&Q) -> T, { - let hash = make_hash(&self.map.hash_builder, value); - let bucket = match self.map.find_or_find_insert_index(hash, value) { - Ok(bucket) => bucket, - Err(index) => { - let new = f(value); - assert!(value.equivalent(&new), "new value is not equivalent"); - unsafe { self.map.table.insert_at_index(hash, index, (new, ())) } - } - }; - unsafe { &bucket.as_ref().0 } + match self.map.entry_ref(value) { + map::EntryRef::Occupied(entry) => entry, + map::EntryRef::Vacant(entry) => entry.insert_entry_with_key(f(value), ()), + } + .into_entry() + .0 } /// Gets the given value's corresponding entry in the set for in-place manipulation.