From 6272247fe5cbc079b4b43b8f2f7a34205682c19f Mon Sep 17 00:00:00 2001 From: djugei Date: Thu, 17 Oct 2024 09:59:17 +0200 Subject: [PATCH 1/2] Add insert_with_key, allowing the user to provide the key at insertion time. --- src/map.rs | 67 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 58 insertions(+), 9 deletions(-) diff --git a/src/map.rs b/src/map.rs index 0cffb9dd8..9d562b6b7 100644 --- a/src/map.rs +++ b/src/map.rs @@ -2983,10 +2983,10 @@ where /// } /// assert!(map["b"] == 20 && map.len() == 2); /// ``` -pub struct VacantEntryRef<'a, 'b, K, Q: ?Sized, V, S, A: Allocator = Global> { +pub struct VacantEntryRef<'map, 'key, K, Q: ?Sized, V, S, A: Allocator = Global> { hash: u64, - key: &'b Q, - table: &'a mut HashMap, + key: &'key Q, + table: &'map mut HashMap, } impl Debug for VacantEntryRef<'_, '_, K, Q, V, S, A> @@ -4332,7 +4332,7 @@ impl<'a, 'b, K, Q: ?Sized, V: Default, S, A: Allocator> EntryRef<'a, 'b, K, Q, V } } -impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'a, 'b, K, Q, V, S, A> { +impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, Q, V, S, A> { /// Gets a reference to the key that would be used when inserting a value /// through the `VacantEntryRef`. /// @@ -4346,7 +4346,7 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'a, 'b, K, Q, V, S /// assert_eq!(map.entry_ref(key).key(), "poneyland"); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn key(&self) -> &'b Q { + pub fn key(&self) -> &'key Q { self.key } @@ -4368,10 +4368,10 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'a, 'b, K, Q, V, S /// assert_eq!(map["poneyland"], 37); /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn insert(self, value: V) -> &'a mut V + pub fn insert(self, value: V) -> &'map mut V where K: Hash, - &'b Q: Into, + &'key Q: Into, S: BuildHasher, { let table = &mut self.table.table; @@ -4383,6 +4383,55 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'a, 'b, K, Q, V, S &mut entry.1 } + /// [insert](Self::insert) a value by providing the key at insertion time. + /// Returns a mutable reference to the inserted value. + /// + /// Useful if Into is not implemented for converting from &Q to K. + /// ```compile_fail + /// # use hashbrown::hash_map::EntryRef; + /// # use hashbrown::HashMap; + /// // note that our key is a (small) tuple + /// let mut h = HashMap::<(String,), char>::new(); + /// let k = (String::from("c"),); + /// match h.entry_ref(&k) { + /// // fails here, as &(i32) does not implement Into<(i32)> + /// EntryRef::Vacant(r) => r.insert('c'), + /// EntryRef::Occupied(r) => r.get(), + /// }; + /// ``` + /// ``` + /// # use hashbrown::hash_map::EntryRef; + /// # use hashbrown::HashMap; + /// let mut h = HashMap::<(String,), char>::new(); + /// let k = (String::from("c"),); + /// match h.entry_ref(&k) { + /// // works, because we manually clone when needed. + /// EntryRef::Vacant(r) => r.insert_with_key(k.clone(),'c'), + /// // in this branch we avoided the clone + /// EntryRef::Occupied(r) => r.get(), + /// }; + /// ``` + /// + #[cfg_attr(feature = "inline-more", inline)] + pub fn insert_with_key(self, key: K, value: V) -> &'map mut V + where + K: Hash, + Q: Equivalent, + S: BuildHasher, + { + let table = &mut self.table.table; + assert!( + (self.key).equivalent(&key), + "The key used for Entry creation and the one used for insertion are not equivalent" + ); + let entry = table.insert_entry( + self.hash, + (key, value), + make_hasher::<_, V, S>(&self.table.hash_builder), + ); + &mut entry.1 + } + /// Sets the value of the entry with the [`VacantEntryRef`]'s key, /// and returns an [`OccupiedEntry`]. /// @@ -4400,10 +4449,10 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'a, 'b, K, Q, V, S /// } /// ``` #[cfg_attr(feature = "inline-more", inline)] - pub fn insert_entry(self, value: V) -> OccupiedEntry<'a, K, V, S, A> + pub fn insert_entry(self, value: V) -> OccupiedEntry<'map, K, V, S, A> where K: Hash, - &'b Q: Into, + &'key Q: Into, S: BuildHasher, { let elem = self.table.table.insert( From 2712a86652bff2d869c7fa42af0c4554387f91c3 Mon Sep 17 00:00:00 2001 From: Amanieu d'Antras Date: Wed, 19 Nov 2025 00:53:26 +0000 Subject: [PATCH 2/2] Clean up doc comment for `VacantEntryRef::insert_with_key` --- src/map.rs | 54 +++++++++++++++++++++++++++--------------------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/map.rs b/src/map.rs index 9d562b6b7..02917c719 100644 --- a/src/map.rs +++ b/src/map.rs @@ -4383,35 +4383,35 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, &mut entry.1 } - /// [insert](Self::insert) a value by providing the key at insertion time. - /// Returns a mutable reference to the inserted value. - /// - /// Useful if Into is not implemented for converting from &Q to K. - /// ```compile_fail - /// # use hashbrown::hash_map::EntryRef; - /// # use hashbrown::HashMap; - /// // note that our key is a (small) tuple - /// let mut h = HashMap::<(String,), char>::new(); - /// let k = (String::from("c"),); - /// match h.entry_ref(&k) { - /// // fails here, as &(i32) does not implement Into<(i32)> - /// EntryRef::Vacant(r) => r.insert('c'), - /// EntryRef::Occupied(r) => r.get(), - /// }; - /// ``` + /// Sets the key and value of the entry and returns a mutable reference to + /// the inserted value. + /// + /// Unlike [`VacantEntryRef::insert`], 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 h = HashMap::<(String,), char>::new(); - /// let k = (String::from("c"),); - /// match h.entry_ref(&k) { - /// // works, because we manually clone when needed. - /// EntryRef::Vacant(r) => r.insert_with_key(k.clone(),'c'), - /// // in this branch we avoided the clone - /// EntryRef::Occupied(r) => r.get(), + /// 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 v = 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_with_key(k.clone(), 'c'), + /// // In this branch we avoid the clone. + /// EntryRef::Occupied(r) => r.into_mut(), /// }; + /// assert_eq!(*v, 'c'); /// ``` - /// #[cfg_attr(feature = "inline-more", inline)] pub fn insert_with_key(self, key: K, value: V) -> &'map mut V where @@ -4422,7 +4422,7 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K, let table = &mut self.table.table; assert!( (self.key).equivalent(&key), - "The key used for Entry creation and the one used for insertion are not equivalent" + "key used for Entry creation is not equivalent to the one used for insertion" ); let entry = table.insert_entry( self.hash,