diff --git a/src/map.rs b/src/map.rs index 1e7fb61a9b..0fa7216b4c 100644 --- a/src/map.rs +++ b/src/map.rs @@ -1285,6 +1285,41 @@ where } } + /// Tries to insert a key-value pair into the map, and returns + /// a mutable reference to the value in the entry. + /// + /// # Errors + /// + /// If the map already had this key present, nothing is updated, and + /// an error containing the occupied entry and the value is returned. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use hashbrown::HashMap; + /// + /// let mut map = HashMap::new(); + /// assert_eq!(map.try_insert(37, "a").unwrap(), &"a"); + /// + /// let err = map.try_insert(37, "b").unwrap_err(); + /// assert_eq!(err.entry.key(), &37); + /// assert_eq!(err.entry.get(), &"a"); + /// assert_eq!(err.value, "b"); + /// ``` + #[cfg_attr(feature = "inline-more", inline)] + pub fn try_insert( + &mut self, + key: K, + value: V, + ) -> Result<&mut V, OccupiedError<'_, K, V, S, A>> { + match self.entry(key) { + Entry::Occupied(entry) => Err(OccupiedError { entry, value }), + Entry::Vacant(entry) => Ok(entry.insert(value)), + } + } + /// Removes a key from the map, returning the value at the key if the key /// was previously in the map. /// @@ -2394,6 +2429,40 @@ impl Debug for VacantEntry<'_, K, V, S, A> } } +/// The error returned by [`try_insert`](HashMap::try_insert) when the key already exists. +/// +/// Contains the occupied entry, and the value that was not inserted. +pub struct OccupiedError<'a, K, V, S, A: Allocator + Clone = Global> { + /// The entry in the map that was already occupied. + pub entry: OccupiedEntry<'a, K, V, S, A>, + /// The value which was not inserted, because the entry was already occupied. + pub value: V, +} + +impl Debug for OccupiedError<'_, K, V, S, A> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("OccupiedError") + .field("key", self.entry.key()) + .field("old_value", self.entry.get()) + .field("new_value", &self.value) + .finish() + } +} + +impl<'a, K: Debug, V: Debug, S, A: Allocator + Clone> fmt::Display + for OccupiedError<'a, K, V, S, A> +{ + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "failed to insert {:?}, key {:?} already exists with value {:?}", + self.value, + self.entry.key(), + self.entry.get(), + ) + } +} + impl<'a, K, V, S, A: Allocator + Clone> IntoIterator for &'a HashMap { type Item = (&'a K, &'a V); type IntoIter = Iter<'a, K, V>;