Skip to content

Commit 1e7d7c4

Browse files
committed
Improve entry API
* Add hash_map::{OccupiedEntry::into_entry, VacantEntryRef::insert_entry_with_key} * Make hash_map::EntryRef use ToOwned instead of From/Into
1 parent 4ae9aaa commit 1e7d7c4

File tree

2 files changed

+119
-47
lines changed

2 files changed

+119
-47
lines changed

src/map.rs

Lines changed: 100 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ use crate::raw::{
22
Allocator, Bucket, Global, RawDrain, RawExtractIf, RawIntoIter, RawIter, RawTable,
33
};
44
use crate::{DefaultHashBuilder, Equivalent, TryReserveError};
5+
use ::alloc::borrow::ToOwned;
56
use core::borrow::Borrow;
67
use core::fmt::{self, Debug};
78
use core::hash::{BuildHasher, Hash};
@@ -3963,6 +3964,42 @@ impl<'a, K, V, S, A: Allocator> OccupiedEntry<'a, K, V, S, A> {
39633964
unsafe { &mut self.elem.as_mut().1 }
39643965
}
39653966

3967+
/// Converts the `OccupiedEntry` into a reference to the key and a
3968+
/// mutable reference to the value in the entry with a lifetime bound to the
3969+
/// map itself.
3970+
///
3971+
/// If you need multiple references to the `OccupiedEntry`, see [`key`] and
3972+
/// [`get_mut`].
3973+
///
3974+
/// [`key`]: Self::key
3975+
/// [`get_mut`]: Self::get_mut
3976+
///
3977+
/// # Examples
3978+
///
3979+
/// ```
3980+
/// use hashbrown::hash_map::{Entry, HashMap};
3981+
///
3982+
/// let mut map: HashMap<&str, u32> = HashMap::new();
3983+
/// map.entry("poneyland").or_insert(12);
3984+
///
3985+
/// assert_eq!(map["poneyland"], 12);
3986+
///
3987+
/// let key_val: (&&str, &mut u32);
3988+
/// match map.entry("poneyland") {
3989+
/// Entry::Occupied(entry) => key_val = entry.into_entry(),
3990+
/// Entry::Vacant(_) => panic!(),
3991+
/// }
3992+
/// *key_val.1 += 10;
3993+
///
3994+
/// assert_eq!(key_val, (&"poneyland", &mut 22));
3995+
/// assert_eq!(map["poneyland"], 22);
3996+
/// ```
3997+
#[cfg_attr(feature = "inline-more", inline)]
3998+
pub fn into_entry(self) -> (&'a K, &'a mut V) {
3999+
let (key, val) = unsafe { self.elem.as_mut() };
4000+
(key, val)
4001+
}
4002+
39664003
/// Sets the value of the entry, and returns the entry's old value.
39674004
///
39684005
/// # Examples
@@ -4212,7 +4249,7 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> EntryRef<'a, 'b, K, Q, V, S, A> {
42124249
pub fn insert(self, value: V) -> OccupiedEntry<'a, K, V, S, A>
42134250
where
42144251
K: Hash,
4215-
&'b Q: Into<K>,
4252+
Q: ToOwned<Owned = K>,
42164253
S: BuildHasher,
42174254
{
42184255
match self {
@@ -4246,7 +4283,7 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> EntryRef<'a, 'b, K, Q, V, S, A> {
42464283
pub fn or_insert(self, default: V) -> &'a mut V
42474284
where
42484285
K: Hash,
4249-
&'b Q: Into<K>,
4286+
Q: ToOwned<Owned = K>,
42504287
S: BuildHasher,
42514288
{
42524289
match self {
@@ -4277,7 +4314,7 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> EntryRef<'a, 'b, K, Q, V, S, A> {
42774314
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V
42784315
where
42794316
K: Hash,
4280-
&'b Q: Into<K>,
4317+
Q: ToOwned<Owned = K>,
42814318
S: BuildHasher,
42824319
{
42834320
match self {
@@ -4309,7 +4346,7 @@ impl<'a, 'b, K, Q: ?Sized, V, S, A: Allocator> EntryRef<'a, 'b, K, Q, V, S, A> {
43094346
pub fn or_insert_with_key<F: FnOnce(&Q) -> V>(self, default: F) -> &'a mut V
43104347
where
43114348
K: Hash + Borrow<Q>,
4312-
&'b Q: Into<K>,
4349+
Q: ToOwned<Owned = K>,
43134350
S: BuildHasher,
43144351
{
43154352
match self {
@@ -4405,7 +4442,7 @@ impl<'a, 'b, K, Q: ?Sized, V: Default, S, A: Allocator> EntryRef<'a, 'b, K, Q, V
44054442
pub fn or_default(self) -> &'a mut V
44064443
where
44074444
K: Hash,
4408-
&'b Q: Into<K>,
4445+
Q: ToOwned<Owned = K>,
44094446
S: BuildHasher,
44104447
{
44114448
match self {
@@ -4438,7 +4475,8 @@ impl<'a, 'b, K, Q: ?Sized, V: Default, S, A: Allocator> EntryRef<'a, 'b, K, Q, V
44384475
#[cfg_attr(feature = "inline-more", inline)]
44394476
pub fn or_default_entry(self) -> OccupiedEntry<'a, K, V, S, A>
44404477
where
4441-
K: Hash + From<&'b Q>,
4478+
K: Hash,
4479+
Q: ToOwned<Owned = K>,
44424480
S: BuildHasher,
44434481
{
44444482
match self {
@@ -4487,13 +4525,13 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K,
44874525
pub fn insert(self, value: V) -> &'map mut V
44884526
where
44894527
K: Hash,
4490-
&'key Q: Into<K>,
4528+
Q: ToOwned<Owned = K>,
44914529
S: BuildHasher,
44924530
{
44934531
let table = &mut self.table.table;
44944532
let entry = table.insert_entry(
44954533
self.hash,
4496-
(self.key.into(), value),
4534+
(self.key.to_owned(), value),
44974535
make_hasher::<_, V, S>(&self.table.hash_builder),
44984536
);
44994537
&mut entry.1
@@ -4535,17 +4573,7 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K,
45354573
Q: Equivalent<K>,
45364574
S: BuildHasher,
45374575
{
4538-
let table = &mut self.table.table;
4539-
assert!(
4540-
(self.key).equivalent(&key),
4541-
"key used for Entry creation is not equivalent to the one used for insertion"
4542-
);
4543-
let entry = table.insert_entry(
4544-
self.hash,
4545-
(key, value),
4546-
make_hasher::<_, V, S>(&self.table.hash_builder),
4547-
);
4548-
&mut entry.1
4576+
self.insert_entry_with_key(key, value).into_mut()
45494577
}
45504578

45514579
/// Sets the value of the entry with the [`VacantEntryRef`]'s key,
@@ -4568,12 +4596,63 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K,
45684596
pub fn insert_entry(self, value: V) -> OccupiedEntry<'map, K, V, S, A>
45694597
where
45704598
K: Hash,
4571-
&'key Q: Into<K>,
4599+
Q: ToOwned<Owned = K>,
4600+
S: BuildHasher,
4601+
{
4602+
let elem = self.table.table.insert(
4603+
self.hash,
4604+
(self.key.to_owned(), value),
4605+
make_hasher::<_, V, S>(&self.table.hash_builder),
4606+
);
4607+
OccupiedEntry {
4608+
hash: self.hash,
4609+
elem,
4610+
table: self.table,
4611+
}
4612+
}
4613+
4614+
/// Sets the key and value of the entry and returns an [`OccupiedEntry`].
4615+
///
4616+
/// Unlike [`VacantEntryRef::insert_entry`], this method allows the key to
4617+
/// be explicitly specified, which is useful for key types that don't
4618+
/// implement `K: From<&Q>`.
4619+
///
4620+
/// # Panics
4621+
///
4622+
/// This method panics if `key` is not equivalent to the key used to create
4623+
/// the `VacantEntryRef`.
4624+
///
4625+
/// # Example
4626+
///
4627+
/// ```
4628+
/// use hashbrown::hash_map::EntryRef;
4629+
/// use hashbrown::HashMap;
4630+
///
4631+
/// let mut map = HashMap::<(String, String), char>::new();
4632+
/// let k = ("c".to_string(), "C".to_string());
4633+
/// let r = match map.entry_ref(&k) {
4634+
/// // Insert cannot be used here because tuples do not implement From.
4635+
/// // However this works because we can manually clone instead.
4636+
/// EntryRef::Vacant(r) => r.insert_entry_with_key(k.clone(), 'c'),
4637+
/// // In this branch we avoid the clone.
4638+
/// EntryRef::Occupied(r) => r,
4639+
/// };
4640+
/// assert_eq!(r.get(), &'c');
4641+
/// ```
4642+
#[cfg_attr(feature = "inline-more", inline)]
4643+
pub fn insert_entry_with_key(self, key: K, value: V) -> OccupiedEntry<'map, K, V, S, A>
4644+
where
4645+
K: Hash,
4646+
Q: Equivalent<K>,
45724647
S: BuildHasher,
45734648
{
4649+
assert!(
4650+
(self.key).equivalent(&key),
4651+
"key used for Entry creation is not equivalent to the one used for insertion"
4652+
);
45744653
let elem = self.table.table.insert(
45754654
self.hash,
4576-
(self.key.into(), value),
4655+
(key, value),
45774656
make_hasher::<_, V, S>(&self.table.hash_builder),
45784657
);
45794658
OccupiedEntry {

src/set.rs

Lines changed: 19 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -912,12 +912,12 @@ where
912912
/// ```
913913
#[cfg_attr(feature = "inline-more", inline)]
914914
pub fn get_or_insert(&mut self, value: T) -> &T {
915-
let hash = make_hash(&self.map.hash_builder, &value);
916-
let bucket = match self.map.find_or_find_insert_index(hash, &value) {
917-
Ok(bucket) => bucket,
918-
Err(index) => unsafe { self.map.table.insert_at_index(hash, index, (value, ())) },
919-
};
920-
unsafe { &bucket.as_ref().0 }
915+
match self.map.entry(value) {
916+
map::Entry::Occupied(entry) => entry,
917+
map::Entry::Vacant(entry) => entry.insert_entry(()),
918+
}
919+
.into_entry()
920+
.0
921921
}
922922

923923
/// Inserts a value computed from `f` into the set if the given `value` is
@@ -951,16 +951,12 @@ where
951951
Q: Hash + Equivalent<T> + ?Sized,
952952
F: FnOnce(&Q) -> T,
953953
{
954-
let hash = make_hash(&self.map.hash_builder, value);
955-
let bucket = match self.map.find_or_find_insert_index(hash, value) {
956-
Ok(bucket) => bucket,
957-
Err(index) => {
958-
let new = f(value);
959-
assert!(value.equivalent(&new), "new value is not equivalent");
960-
unsafe { self.map.table.insert_at_index(hash, index, (new, ())) }
961-
}
962-
};
963-
unsafe { &bucket.as_ref().0 }
954+
match self.map.entry_ref(value) {
955+
map::EntryRef::Occupied(entry) => entry,
956+
map::EntryRef::Vacant(entry) => entry.insert_entry_with_key(f(value), ()),
957+
}
958+
.into_entry()
959+
.0
964960
}
965961

966962
/// Gets the given value's corresponding entry in the set for in-place manipulation.
@@ -1585,16 +1581,13 @@ where
15851581
/// ```
15861582
fn bitxor_assign(&mut self, rhs: &HashSet<T, S, A>) {
15871583
for item in rhs {
1588-
let hash = make_hash(&self.map.hash_builder, item);
1589-
match self.map.find_or_find_insert_index(hash, item) {
1590-
Ok(bucket) => unsafe {
1591-
self.map.table.remove(bucket);
1592-
},
1593-
Err(index) => unsafe {
1594-
self.map
1595-
.table
1596-
.insert_at_index(hash, index, (item.clone(), ()));
1597-
},
1584+
match self.map.entry_ref(item) {
1585+
map::EntryRef::Occupied(entry) => {
1586+
entry.remove();
1587+
}
1588+
map::EntryRef::Vacant(entry) => {
1589+
entry.insert(());
1590+
}
15981591
}
15991592
}
16001593
}

0 commit comments

Comments
 (0)