Skip to content

Commit b6d96ca

Browse files
committed
Change signature of get_many_mut APIs
by 1) panicking on overlapping keys and 2) returning an array of Option rather than an Option of array.
1 parent 7cf51ea commit b6d96ca

File tree

3 files changed

+139
-79
lines changed

3 files changed

+139
-79
lines changed

src/map.rs

Lines changed: 84 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1470,6 +1470,10 @@ where
14701470
/// mutable reference will be returned to any value. `None` will be returned if any of the
14711471
/// keys are duplicates or missing.
14721472
///
1473+
/// # Panics
1474+
///
1475+
/// Panics if some keys are overlapping.
1476+
///
14731477
/// # Examples
14741478
///
14751479
/// ```
@@ -1487,27 +1491,33 @@ where
14871491
/// ]);
14881492
/// assert_eq!(
14891493
/// got,
1490-
/// Some([
1491-
/// &mut 1807,
1492-
/// &mut 1800,
1493-
/// ]),
1494+
/// [
1495+
/// Some(&mut 1807),
1496+
/// Some(&mut 1800),
1497+
/// ],
14941498
/// );
14951499
///
14961500
/// // Missing keys result in None
14971501
/// let got = libraries.get_many_mut([
1498-
/// "Athenæum",
1502+
/// "Athenæum1",
14991503
/// "New York Public Library",
15001504
/// ]);
1501-
/// assert_eq!(got, None);
1505+
/// assert_eq!(got, [None, None]);
1506+
/// ```
1507+
///
1508+
/// ```should_panic
1509+
/// use hashbrown::HashMap;
1510+
///
1511+
/// let mut libraries = HashMap::new();
1512+
/// libraries.insert("Athenæum".to_string(), 1807);
15021513
///
1503-
/// // Duplicate keys result in None
1514+
/// // Duplicate keys panic!
15041515
/// let got = libraries.get_many_mut([
15051516
/// "Athenæum",
15061517
/// "Athenæum",
15071518
/// ]);
1508-
/// assert_eq!(got, None);
15091519
/// ```
1510-
pub fn get_many_mut<Q, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut V; N]>
1520+
pub fn get_many_mut<Q, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N]
15111521
where
15121522
Q: Hash + Equivalent<K> + ?Sized,
15131523
{
@@ -1517,8 +1527,8 @@ where
15171527
/// Attempts to get mutable references to `N` values in the map at once, without validating that
15181528
/// the values are unique.
15191529
///
1520-
/// Returns an array of length `N` with the results of each query. `None` will be returned if
1521-
/// any of the keys are missing.
1530+
/// Returns an array of length `N` with the results of each query. `None` will be used if
1531+
/// the key is missing.
15221532
///
15231533
/// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`).
15241534
///
@@ -1540,29 +1550,31 @@ where
15401550
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
15411551
/// libraries.insert("Library of Congress".to_string(), 1800);
15421552
///
1543-
/// let got = libraries.get_many_mut([
1553+
/// // SAFETY: The keys do not overlap.
1554+
/// let got = unsafe { libraries.get_many_unchecked_mut([
15441555
/// "Athenæum",
15451556
/// "Library of Congress",
1546-
/// ]);
1557+
/// ]) };
15471558
/// assert_eq!(
15481559
/// got,
1549-
/// Some([
1550-
/// &mut 1807,
1551-
/// &mut 1800,
1552-
/// ]),
1560+
/// [
1561+
/// Some(&mut 1807),
1562+
/// Some(&mut 1800),
1563+
/// ],
15531564
/// );
15541565
///
1555-
/// // Missing keys result in None
1556-
/// let got = libraries.get_many_mut([
1566+
/// // SAFETY: The keys do not overlap.
1567+
/// let got = unsafe { libraries.get_many_unchecked_mut([
15571568
/// "Athenæum",
15581569
/// "New York Public Library",
1559-
/// ]);
1560-
/// assert_eq!(got, None);
1570+
/// ]) };
1571+
/// // Missing keys result in None
1572+
/// assert_eq!(got, [Some(&mut 1807), None]);
15611573
/// ```
15621574
pub unsafe fn get_many_unchecked_mut<Q, const N: usize>(
15631575
&mut self,
15641576
ks: [&Q; N],
1565-
) -> Option<[&'_ mut V; N]>
1577+
) -> [Option<&'_ mut V>; N]
15661578
where
15671579
Q: Hash + Equivalent<K> + ?Sized,
15681580
{
@@ -1574,8 +1586,11 @@ where
15741586
/// references to the corresponding keys.
15751587
///
15761588
/// Returns an array of length `N` with the results of each query. For soundness, at most one
1577-
/// mutable reference will be returned to any value. `None` will be returned if any of the keys
1578-
/// are duplicates or missing.
1589+
/// mutable reference will be returned to any value. `None` will be used if the key is missing.
1590+
///
1591+
/// # Panics
1592+
///
1593+
/// Panics if some keys are overlapping.
15791594
///
15801595
/// # Examples
15811596
///
@@ -1594,30 +1609,37 @@ where
15941609
/// ]);
15951610
/// assert_eq!(
15961611
/// got,
1597-
/// Some([
1598-
/// (&"Bodleian Library".to_string(), &mut 1602),
1599-
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
1600-
/// ]),
1612+
/// [
1613+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1614+
/// Some((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
1615+
/// ],
16011616
/// );
16021617
/// // Missing keys result in None
16031618
/// let got = libraries.get_many_key_value_mut([
16041619
/// "Bodleian Library",
16051620
/// "Gewandhaus",
16061621
/// ]);
1607-
/// assert_eq!(got, None);
1622+
/// assert_eq!(got, [Some((&"Bodleian Library".to_string(), &mut 1602)), None]);
1623+
/// ```
1624+
///
1625+
/// ```should_panic
1626+
/// use hashbrown::HashMap;
1627+
///
1628+
/// let mut libraries = HashMap::new();
1629+
/// libraries.insert("Bodleian Library".to_string(), 1602);
1630+
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
16081631
///
1609-
/// // Duplicate keys result in None
1632+
/// // Duplicate keys result in panic!
16101633
/// let got = libraries.get_many_key_value_mut([
16111634
/// "Bodleian Library",
16121635
/// "Herzogin-Anna-Amalia-Bibliothek",
16131636
/// "Herzogin-Anna-Amalia-Bibliothek",
16141637
/// ]);
1615-
/// assert_eq!(got, None);
16161638
/// ```
16171639
pub fn get_many_key_value_mut<Q, const N: usize>(
16181640
&mut self,
16191641
ks: [&Q; N],
1620-
) -> Option<[(&'_ K, &'_ mut V); N]>
1642+
) -> [Option<(&'_ K, &'_ mut V)>; N]
16211643
where
16221644
Q: Hash + Equivalent<K> + ?Sized,
16231645
{
@@ -1657,30 +1679,36 @@ where
16571679
/// ]);
16581680
/// assert_eq!(
16591681
/// got,
1660-
/// Some([
1661-
/// (&"Bodleian Library".to_string(), &mut 1602),
1662-
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
1663-
/// ]),
1682+
/// [
1683+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1684+
/// Some((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
1685+
/// ],
16641686
/// );
16651687
/// // Missing keys result in None
16661688
/// let got = libraries.get_many_key_value_mut([
16671689
/// "Bodleian Library",
16681690
/// "Gewandhaus",
16691691
/// ]);
1670-
/// assert_eq!(got, None);
1692+
/// assert_eq!(
1693+
/// got,
1694+
/// [
1695+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1696+
/// None,
1697+
/// ],
1698+
/// );
16711699
/// ```
16721700
pub unsafe fn get_many_key_value_unchecked_mut<Q, const N: usize>(
16731701
&mut self,
16741702
ks: [&Q; N],
1675-
) -> Option<[(&'_ K, &'_ mut V); N]>
1703+
) -> [Option<(&'_ K, &'_ mut V)>; N]
16761704
where
16771705
Q: Hash + Equivalent<K> + ?Sized,
16781706
{
16791707
self.get_many_unchecked_mut_inner(ks)
16801708
.map(|res| res.map(|(k, v)| (&*k, v)))
16811709
}
16821710

1683-
fn get_many_mut_inner<Q, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut (K, V); N]>
1711+
fn get_many_mut_inner<Q, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut (K, V)>; N]
16841712
where
16851713
Q: Hash + Equivalent<K> + ?Sized,
16861714
{
@@ -1692,7 +1720,7 @@ where
16921720
unsafe fn get_many_unchecked_mut_inner<Q, const N: usize>(
16931721
&mut self,
16941722
ks: [&Q; N],
1695-
) -> Option<[&'_ mut (K, V); N]>
1723+
) -> [Option<&'_ mut (K, V)>; N]
16961724
where
16971725
Q: Hash + Equivalent<K> + ?Sized,
16981726
{
@@ -5937,33 +5965,39 @@ mod test_map {
59375965
}
59385966

59395967
#[test]
5940-
fn test_get_each_mut() {
5968+
fn test_get_many_mut() {
59415969
let mut map = HashMap::new();
59425970
map.insert("foo".to_owned(), 0);
59435971
map.insert("bar".to_owned(), 10);
59445972
map.insert("baz".to_owned(), 20);
59455973
map.insert("qux".to_owned(), 30);
59465974

59475975
let xs = map.get_many_mut(["foo", "qux"]);
5948-
assert_eq!(xs, Some([&mut 0, &mut 30]));
5976+
assert_eq!(xs, [Some(&mut 0), Some(&mut 30)]);
59495977

59505978
let xs = map.get_many_mut(["foo", "dud"]);
5951-
assert_eq!(xs, None);
5952-
5953-
let xs = map.get_many_mut(["foo", "foo"]);
5954-
assert_eq!(xs, None);
5979+
assert_eq!(xs, [Some(&mut 0), None]);
59555980

59565981
let ys = map.get_many_key_value_mut(["bar", "baz"]);
59575982
assert_eq!(
59585983
ys,
5959-
Some([(&"bar".to_owned(), &mut 10), (&"baz".to_owned(), &mut 20),]),
5984+
[
5985+
Some((&"bar".to_owned(), &mut 10)),
5986+
Some((&"baz".to_owned(), &mut 20))
5987+
],
59605988
);
59615989

59625990
let ys = map.get_many_key_value_mut(["bar", "dip"]);
5963-
assert_eq!(ys, None);
5991+
assert_eq!(ys, [Some((&"bar".to_string(), &mut 10)), None]);
5992+
}
5993+
5994+
#[test]
5995+
#[should_panic = "duplicate keys found"]
5996+
fn test_get_many_mut_duplicate() {
5997+
let mut map = HashMap::new();
5998+
map.insert("foo".to_owned(), 0);
59645999

5965-
let ys = map.get_many_key_value_mut(["baz", "baz"]);
5966-
assert_eq!(ys, None);
6000+
let _xs = map.get_many_mut(["foo", "foo"]);
59676001
}
59686002

59696003
#[test]

src/raw/mod.rs

Lines changed: 19 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1291,48 +1291,52 @@ impl<T, A: Allocator> RawTable<T, A> {
12911291
&mut self,
12921292
hashes: [u64; N],
12931293
eq: impl FnMut(usize, &T) -> bool,
1294-
) -> Option<[&'_ mut T; N]> {
1294+
) -> [Option<&'_ mut T>; N] {
12951295
unsafe {
1296-
let ptrs = self.get_many_mut_pointers(hashes, eq)?;
1296+
let ptrs = self.get_many_mut_pointers(hashes, eq);
12971297

12981298
for (i, &cur) in ptrs.iter().enumerate() {
1299-
if ptrs[..i].iter().any(|&prev| ptr::eq::<T>(prev, cur)) {
1300-
return None;
1299+
if let Some(cur) = cur {
1300+
if ptrs[..i]
1301+
.iter()
1302+
.flatten()
1303+
.any(|&prev| ptr::eq::<T>(prev, cur))
1304+
{
1305+
panic!("duplicate keys found");
1306+
}
13011307
}
13021308
}
13031309
// All bucket are distinct from all previous buckets so we're clear to return the result
13041310
// of the lookup.
13051311

1306-
// TODO use `MaybeUninit::array_assume_init` here instead once that's stable.
1307-
Some(mem::transmute_copy(&ptrs))
1312+
ptrs.map(|ptr| ptr.map(|ptr| &mut *ptr))
13081313
}
13091314
}
13101315

13111316
pub unsafe fn get_many_unchecked_mut<const N: usize>(
13121317
&mut self,
13131318
hashes: [u64; N],
13141319
eq: impl FnMut(usize, &T) -> bool,
1315-
) -> Option<[&'_ mut T; N]> {
1316-
let ptrs = self.get_many_mut_pointers(hashes, eq)?;
1317-
Some(mem::transmute_copy(&ptrs))
1320+
) -> [Option<&'_ mut T>; N] {
1321+
let ptrs = self.get_many_mut_pointers(hashes, eq);
1322+
ptrs.map(|ptr| ptr.map(|ptr| &mut *ptr))
13181323
}
13191324

13201325
unsafe fn get_many_mut_pointers<const N: usize>(
13211326
&mut self,
13221327
hashes: [u64; N],
13231328
mut eq: impl FnMut(usize, &T) -> bool,
1324-
) -> Option<[*mut T; N]> {
1329+
) -> [Option<*mut T>; N] {
13251330
// TODO use `MaybeUninit::uninit_array` here instead once that's stable.
1326-
let mut outs: MaybeUninit<[*mut T; N]> = MaybeUninit::uninit();
1331+
let mut outs: MaybeUninit<[Option<*mut T>; N]> = MaybeUninit::uninit();
13271332
let outs_ptr = outs.as_mut_ptr();
13281333

13291334
for (i, &hash) in hashes.iter().enumerate() {
1330-
let cur = self.find(hash, |k| eq(i, k))?;
1331-
*(*outs_ptr).get_unchecked_mut(i) = cur.as_mut();
1335+
let cur = self.find(hash, |k| eq(i, k)).map(|cur| cur.as_ptr());
1336+
*(*outs_ptr).get_unchecked_mut(i) = cur;
13321337
}
13331338

1334-
// TODO use `MaybeUninit::array_assume_init` here instead once that's stable.
1335-
Some(outs.assume_init())
1339+
outs.assume_init()
13361340
}
13371341

13381342
/// Returns the number of elements the map can hold without reallocating.

0 commit comments

Comments
 (0)