Skip to content

Commit 0de2c9a

Browse files
committed
implement entry API for HashMap
Deprecates the `find_or_*` family of "internal mutation" methods on `HashMap` in favour of the "external mutation" Entry API as part of RFC 60. Part of rust-lang#17320, although this still needs to be done on `TreeMap` and `BTree`. Work on `TreeMap` is TBD, and `BTree`'s work is part of the complete rewrite in rust-lang#17334. The implemented API deviates from the API described in the RFC in two key places: * `VacantEntry.set` yields a mutable reference to the inserted element to avoid code duplication where complex logic needs to be done *regardless* of whether the entry was vacant or not. * `OccupiedEntry.into_mut` was added so that it is possible to return a reference into the map beyond the lifetime of the Entry itself, providing functional parity to `VacantEntry.set`. [breaking-change]
1 parent d485e87 commit 0de2c9a

File tree

2 files changed

+222
-6
lines changed

2 files changed

+222
-6
lines changed

src/libstd/collections/hashmap/map.rs

Lines changed: 217 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -25,11 +25,13 @@ use ops::{Deref, DerefMut};
2525
use option::{Some, None, Option};
2626
use result::{Ok, Err};
2727
use ops::Index;
28+
use core::result::Result;
2829

2930
use super::table;
3031
use super::table::{
3132
Bucket,
3233
Empty,
34+
EmptyBucket,
3335
Full,
3436
FullBucket,
3537
FullBucketImm,
@@ -328,11 +330,11 @@ fn search_hashed<K: Eq, V, M: Deref<RawTable<K, V>>>(table: M, hash: &SafeHash,
328330
search_hashed_generic(table, hash, |k_| *k == *k_)
329331
}
330332

331-
fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> V {
332-
let (empty, _k, retval) = starting_bucket.take();
333+
fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> (K, V) {
334+
let (empty, retkey, retval) = starting_bucket.take();
333335
let mut gap = match empty.gap_peek() {
334336
Some(b) => b,
335-
None => return retval
337+
None => return (retkey, retval)
336338
};
337339

338340
while gap.full().distance() != 0 {
@@ -343,7 +345,7 @@ fn pop_internal<K, V>(starting_bucket: FullBucketMut<K, V>) -> V {
343345
}
344346

345347
// Now we've done all our shifting. Return the value we grabbed earlier.
346-
return retval;
348+
return (retkey, retval);
347349
}
348350

349351
/// Perform robin hood bucket stealing at the given `bucket`. You must
@@ -567,7 +569,8 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> MutableMap<K, V> for HashMap<K, V, H>
567569
self.make_some_room(potential_new_size);
568570

569571
self.search_mut(k).map(|bucket| {
570-
pop_internal(bucket)
572+
let (_k, val) = pop_internal(bucket);
573+
val
571574
})
572575
}
573576
}
@@ -867,6 +870,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
867870
/// // Find the existing key
868871
/// assert_eq!(*map.find_or_insert("a", -2), 1);
869872
/// ```
873+
#[deprecated = "use entry instead"]
870874
pub fn find_or_insert(&mut self, k: K, v: V) -> &mut V {
871875
self.find_with_or_insert_with(k, v, |_k, _v, _a| (), |_k, a| a)
872876
}
@@ -886,6 +890,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
886890
/// // Find the existing key
887891
/// assert_eq!(*map.find_or_insert_with(2, |&key| key as uint), 10);
888892
/// ```
893+
#[deprecated = "use entry instead"]
889894
pub fn find_or_insert_with<'a>(&'a mut self, k: K, f: |&K| -> V)
890895
-> &'a mut V {
891896
self.find_with_or_insert_with(k, (), |_k, _v, _a| (), |k, _a| f(k))
@@ -908,6 +913,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
908913
/// assert_eq!(*map.insert_or_update_with("a", 9, |_key, val| *val = 7), 7);
909914
/// assert_eq!(map["a"], 7);
910915
/// ```
916+
#[deprecated = "use entry instead"]
911917
pub fn insert_or_update_with<'a>(
912918
&'a mut self,
913919
k: K,
@@ -965,6 +971,7 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
965971
/// assert_eq!(map["b key"], vec!["new value"]);
966972
/// assert_eq!(map["z key"], vec!["new value", "value"]);
967973
/// ```
974+
#[deprecated = "use entry instead"]
968975
pub fn find_with_or_insert_with<'a, A>(&'a mut self,
969976
k: K,
970977
a: A,
@@ -1124,7 +1131,8 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
11241131

11251132
match self.search_equiv_mut(k) {
11261133
Some(bucket) => {
1127-
Some(pop_internal(bucket))
1134+
let (_k, val) = pop_internal(bucket);
1135+
Some(val)
11281136
}
11291137
_ => None
11301138
}
@@ -1254,6 +1262,70 @@ impl<K: Eq + Hash<S>, V, S, H: Hasher<S>> HashMap<K, V, H> {
12541262
inner: self.table.into_iter().map(|(_, k, v)| (k, v))
12551263
}
12561264
}
1265+
1266+
/// Gets the given key's corresponding entry in the map for in-place manipulation
1267+
pub fn entry<'a>(&'a mut self, key: K) -> Entry<'a, K, V> {
1268+
// Gotta resize now, and we don't know which direction, so try both?
1269+
let size = self.table.size();
1270+
self.make_some_room(size + 1);
1271+
if size > 0 {
1272+
self.make_some_room(size - 1);
1273+
}
1274+
1275+
let hash = self.make_hash(&key);
1276+
search_entry_hashed(&mut self.table, hash, key)
1277+
}
1278+
}
1279+
1280+
fn search_entry_hashed<'a, K: Eq, V>(table: &'a mut RawTable<K,V>, hash: SafeHash, k: K)
1281+
-> Entry<'a, K, V>{
1282+
// Worst case, we'll find one empty bucket among `size + 1` buckets.
1283+
let size = table.size();
1284+
let mut probe = Bucket::new(table, &hash);
1285+
let ib = probe.index();
1286+
1287+
loop {
1288+
let bucket = match probe.peek() {
1289+
Empty(bucket) => {
1290+
// Found a hole!
1291+
return Vacant( VacantEntry {
1292+
hash: hash,
1293+
key: k,
1294+
elem: NoElem(bucket),
1295+
});
1296+
},
1297+
Full(bucket) => bucket
1298+
};
1299+
1300+
if bucket.hash() == hash {
1301+
let is_eq = {
1302+
let (bucket_k, _) = bucket.read();
1303+
k == *bucket_k
1304+
};
1305+
1306+
if is_eq {
1307+
return Occupied( OccupiedEntry{
1308+
hash: hash,
1309+
key: k,
1310+
elem: bucket,
1311+
});
1312+
}
1313+
}
1314+
1315+
let robin_ib = bucket.index() as int - bucket.distance() as int;
1316+
1317+
if (ib as int) < robin_ib {
1318+
// Found a luckier bucket than me. Better steal his spot.
1319+
return Vacant( VacantEntry {
1320+
hash: hash,
1321+
key: k,
1322+
elem: NeqElem(bucket, robin_ib as uint),
1323+
});
1324+
}
1325+
1326+
probe = bucket.next();
1327+
assert!(probe.index() != ib + size + 1);
1328+
}
12571329
}
12581330

12591331
impl<K: Eq + Hash<S>, V: Clone, S, H: Hasher<S>> HashMap<K, V, H> {
@@ -1353,6 +1425,34 @@ pub struct MoveEntries<K, V> {
13531425
inner: iter::Map<'static, (SafeHash, K, V), (K, V), table::MoveEntries<K, V>>
13541426
}
13551427

1428+
/// A view into a single occupied location in a HashMap
1429+
pub struct OccupiedEntry<'a, K:'a, V:'a> {
1430+
hash: SafeHash,
1431+
key: K,
1432+
elem: FullBucket<K, V, &'a mut RawTable<K, V>>,
1433+
}
1434+
1435+
/// A view into a single empty location in a HashMap
1436+
pub struct VacantEntry<'a, K:'a, V:'a> {
1437+
hash: SafeHash,
1438+
key: K,
1439+
elem: VacantEntryState<K,V, &'a mut RawTable<K, V>>,
1440+
}
1441+
1442+
/// A view into a single location in a map, which may be vacant or occupied
1443+
pub enum Entry<'a, K:'a, V:'a> {
1444+
/// An occupied Entry
1445+
Occupied(OccupiedEntry<'a, K, V>),
1446+
/// A vacant Entry
1447+
Vacant(VacantEntry<'a, K, V>),
1448+
}
1449+
1450+
/// Possible states of a VacantEntry
1451+
enum VacantEntryState<K, V, M> {
1452+
NeqElem(FullBucket<K, V, M>, uint),
1453+
NoElem(EmptyBucket<K, V, M>),
1454+
}
1455+
13561456
impl<'a, K, V> Iterator<(&'a K, &'a V)> for Entries<'a, K, V> {
13571457
#[inline]
13581458
fn next(&mut self) -> Option<(&'a K, &'a V)> {
@@ -1386,6 +1486,64 @@ impl<K, V> Iterator<(K, V)> for MoveEntries<K, V> {
13861486
}
13871487
}
13881488

1489+
impl<'a, K, V> OccupiedEntry<'a, K, V> {
1490+
/// Gets a reference to the value in the entry
1491+
pub fn get(&self) -> &V {
1492+
let (_, v) = self.elem.read();
1493+
v
1494+
}
1495+
1496+
/// Gets a mutable reference to the value in the entry
1497+
pub fn get_mut(&mut self) -> &mut V {
1498+
let (_, v) = self.elem.read_mut();
1499+
v
1500+
}
1501+
1502+
/// Converts the OccupiedEntry into a mutable reference to the value in the entry
1503+
/// with a lifetime bound to the map itself
1504+
pub fn into_mut(self) -> &'a mut V {
1505+
let (_, v) = self.elem.into_mut_refs();
1506+
v
1507+
}
1508+
1509+
/// Sets the value of the entry with the OccupiedEntry's key, and returns the entry's old value
1510+
pub fn set(mut self, value: V) -> V {
1511+
let (_, _, v) = self.elem.replace(self.hash, self.key, value);
1512+
v
1513+
}
1514+
1515+
/// Takes the value out of the entry, and returns it
1516+
pub fn take(self) -> V {
1517+
let (_, _, v) = self.elem.take();
1518+
v
1519+
}
1520+
}
1521+
1522+
impl<'a, K, V> VacantEntry<'a, K, V> {
1523+
/// Sets the value of the entry with the VacantEntry's key,
1524+
/// and returns a mutable reference to it
1525+
pub fn set(self, value: V) -> &'a mut V {
1526+
match self.elem {
1527+
NeqElem(mut bucket, ib) => {
1528+
// Capture a mutable ptr to bucket's contents now, since robin_hood will
1529+
// steal the bucket, but we know that this is the location the inserted
1530+
// value can be found at afterwards
1531+
let val_ptr = {
1532+
let (_, v) = bucket.read_mut();
1533+
v as *mut _
1534+
};
1535+
robin_hood(bucket, ib, self.hash, self.key, value);
1536+
unsafe { &mut *val_ptr }
1537+
}
1538+
NoElem(bucket) => {
1539+
let full = bucket.put(self.hash, self.key, value);
1540+
let (_, v) = full.into_mut_refs();
1541+
v
1542+
}
1543+
}
1544+
}
1545+
}
1546+
13891547
/// HashMap keys iterator
13901548
pub type Keys<'a, K, V> =
13911549
iter::Map<'static, (&'a K, &'a V), &'a K, Entries<'a, K, V>>;
@@ -1416,6 +1574,7 @@ mod test_map {
14161574
use prelude::*;
14171575

14181576
use super::HashMap;
1577+
use super::{Occupied, Vacant};
14191578
use cmp::Equiv;
14201579
use hash;
14211580
use iter::{Iterator,range_inclusive,range_step_inclusive};
@@ -2026,4 +2185,56 @@ mod test_map {
20262185

20272186
map[4];
20282187
}
2188+
2189+
#[test]
2190+
fn test_entry(){
2191+
let xs = [(1i, 10i), (2, 20), (3, 30), (4, 40), (5, 50), (6, 60)];
2192+
2193+
let mut map: HashMap<int, int> = xs.iter().map(|&x| x).collect();
2194+
2195+
// Existing key (insert)
2196+
match map.entry(1) {
2197+
Vacant(_) => unreachable!(),
2198+
Occupied(view) => {
2199+
assert_eq!(view.get(), &10);
2200+
assert_eq!(view.set(100), 10);
2201+
}
2202+
}
2203+
assert_eq!(map.find(&1).unwrap(), &100);
2204+
assert_eq!(map.len(), 6);
2205+
2206+
2207+
// Existing key (update)
2208+
match map.entry(2) {
2209+
Vacant(_) => unreachable!(),
2210+
Occupied(mut view) => {
2211+
let v = view.get_mut();
2212+
let new_v = (*v) * 10;
2213+
*v = new_v;
2214+
}
2215+
}
2216+
assert_eq!(map.find(&2).unwrap(), &200);
2217+
assert_eq!(map.len(), 6);
2218+
2219+
// Existing key (take)
2220+
match map.entry(3) {
2221+
Vacant(_) => unreachable!(),
2222+
Occupied(view) => {
2223+
assert_eq!(view.take(), 30);
2224+
}
2225+
}
2226+
assert_eq!(map.find(&3), None);
2227+
assert_eq!(map.len(), 5);
2228+
2229+
2230+
// Inexistent key (insert)
2231+
match map.entry(10) {
2232+
Occupied(_) => unreachable!(),
2233+
Vacant(view) => {
2234+
assert_eq!(*view.set(1000), 1000);
2235+
}
2236+
}
2237+
assert_eq!(map.find(&10).unwrap(), &1000);
2238+
assert_eq!(map.len(), 6);
2239+
}
20292240
}

src/libstd/collections/hashmap/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,11 @@ pub use self::map::HashMap;
1414
pub use self::map::Entries;
1515
pub use self::map::MutEntries;
1616
pub use self::map::MoveEntries;
17+
pub use self::map::Entry;
18+
pub use self::map::Occupied;
19+
pub use self::map::Vacant;
20+
pub use self::map::OccupiedEntry;
21+
pub use self::map::VacantEntry;
1722
pub use self::map::Keys;
1823
pub use self::map::Values;
1924
pub use self::map::INITIAL_CAPACITY;

0 commit comments

Comments
 (0)