Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
99 changes: 88 additions & 11 deletions src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -4535,17 +4571,7 @@ impl<'map, 'key, K, Q: ?Sized, V, S, A: Allocator> VacantEntryRef<'map, 'key, K,
Q: Equivalent<K>,
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,
Expand Down Expand Up @@ -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<K>,
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<K, V, S, A> FromIterator<(K, V)> for HashMap<K, V, S, A>
Expand Down
28 changes: 12 additions & 16 deletions src/set.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -951,16 +951,12 @@ where
Q: Hash + Equivalent<T> + ?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.
Expand Down