Skip to content

Commit 16d0f37

Browse files
committed
Add HashMap::rustc_try_insert
This works like `try_insert` but with `RustcOccupiedEntry` in the error case. The error also contains the attempted key as requested in the upstream `try_insert` tracking issue.
1 parent 18a04c5 commit 16d0f37

1 file changed

Lines changed: 66 additions & 1 deletion

File tree

src/rustc_entry.rs

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use self::RustcEntry::*;
22
use crate::alloc::{Allocator, Global};
3-
use crate::map::{Drain, HashMap, IntoIter, Iter, IterMut, make_hash};
3+
use crate::map::{Drain, HashMap, IntoIter, Iter, IterMut, make_hash, make_hasher};
44
use crate::raw::{Bucket, RawTable};
55
use core::fmt::{self, Debug};
66
use core::hash::{BuildHasher, Hash};
@@ -52,7 +52,72 @@ where
5252
})
5353
}
5454
}
55+
56+
/// Tries to insert a key-value pair into the map, and returns
57+
/// a mutable reference to the value in the entry.
58+
///
59+
/// # Errors
60+
///
61+
/// If the map already had this key present, nothing is updated, and an error
62+
/// containing the occupied entry, the key, and the value is returned.
63+
///
64+
/// # Examples
65+
///
66+
/// Basic usage:
67+
///
68+
/// ```
69+
/// use hashbrown::HashMap;
70+
/// use hashbrown::hash_map::RustcOccupiedError;
71+
///
72+
/// let mut map = HashMap::new();
73+
/// assert_eq!(map.rustc_try_insert(37, "a").ok().unwrap(), &"a");
74+
///
75+
/// match map.rustc_try_insert(37, "b") {
76+
/// Err(RustcOccupiedError { entry, key, value, .. }) => {
77+
/// assert_eq!(entry.key(), &37);
78+
/// assert_eq!(entry.get(), &"a");
79+
/// assert_eq!(key, 37);
80+
/// assert_eq!(value, "b");
81+
/// }
82+
/// _ => panic!()
83+
/// }
84+
/// ```
85+
#[cfg_attr(feature = "inline-more", inline)]
86+
pub fn rustc_try_insert(
87+
&mut self,
88+
key: K,
89+
value: V,
90+
) -> Result<&mut V, RustcOccupiedError<'_, K, V, A>> {
91+
let hash = make_hash(&self.hash_builder, &key);
92+
if let Some(elem) = self.table.find(hash, |q| q.0.eq(&key)) {
93+
let entry = RustcOccupiedEntry {
94+
elem,
95+
table: &mut self.table,
96+
};
97+
Err(RustcOccupiedError { entry, key, value })
98+
} else {
99+
let hasher = make_hasher(&self.hash_builder);
100+
let entry = self.table.insert_entry(hash, (key, value), hasher);
101+
Ok(&mut entry.1)
102+
}
103+
}
104+
}
105+
106+
/// The error returned by [`rustc_try_insert`](HashMap::rustc_try_insert) when the key already exists.
107+
#[non_exhaustive]
108+
pub struct RustcOccupiedError<'a, K, V, A = Global>
109+
where
110+
A: Allocator,
111+
{
112+
/// The entry in the map that was already occupied.
113+
pub entry: RustcOccupiedEntry<'a, K, V, A>,
114+
/// The key which was not inserted, because the entry was already occupied.
115+
pub key: K,
116+
/// The value which was not inserted, because the entry was already occupied.
117+
pub value: V,
55118
}
119+
// NB: there are no impls for this type, because we expect std will
120+
// immediately reconstruct these errors into its own `OccupiedError`.
56121

57122
/// A view into a single entry in a map, which may either be vacant or occupied.
58123
///

0 commit comments

Comments
 (0)