Skip to content

Commit 5f59023

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 5f59023

File tree

3 files changed

+143
-84
lines changed

3 files changed

+143
-84
lines changed

src/map.rs

Lines changed: 85 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1467,8 +1467,11 @@ where
14671467
/// Attempts to get mutable references to `N` values in the map at once.
14681468
///
14691469
/// Returns an array of length `N` with the results of each query. For soundness, at most one
1470-
/// mutable reference will be returned to any value. `None` will be returned if any of the
1471-
/// keys are duplicates or missing.
1470+
/// mutable reference will be returned to any value. `None` will be used if the key is missing.
1471+
///
1472+
/// # Panics
1473+
///
1474+
/// Panics if some keys are overlapping.
14721475
///
14731476
/// # Examples
14741477
///
@@ -1487,27 +1490,33 @@ where
14871490
/// ]);
14881491
/// assert_eq!(
14891492
/// got,
1490-
/// Some([
1491-
/// &mut 1807,
1492-
/// &mut 1800,
1493-
/// ]),
1493+
/// [
1494+
/// Some(&mut 1807),
1495+
/// Some(&mut 1800),
1496+
/// ],
14941497
/// );
14951498
///
14961499
/// // Missing keys result in None
14971500
/// let got = libraries.get_many_mut([
1498-
/// "Athenæum",
1501+
/// "Athenæum1",
14991502
/// "New York Public Library",
15001503
/// ]);
1501-
/// assert_eq!(got, None);
1504+
/// assert_eq!(got, [None, None]);
1505+
/// ```
1506+
///
1507+
/// ```should_panic
1508+
/// use hashbrown::HashMap;
1509+
///
1510+
/// let mut libraries = HashMap::new();
1511+
/// libraries.insert("Athenæum".to_string(), 1807);
15021512
///
1503-
/// // Duplicate keys result in None
1513+
/// // Duplicate keys panic!
15041514
/// let got = libraries.get_many_mut([
15051515
/// "Athenæum",
15061516
/// "Athenæum",
15071517
/// ]);
1508-
/// assert_eq!(got, None);
15091518
/// ```
1510-
pub fn get_many_mut<Q, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut V; N]>
1519+
pub fn get_many_mut<Q, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N]
15111520
where
15121521
Q: Hash + Equivalent<K> + ?Sized,
15131522
{
@@ -1517,8 +1526,8 @@ where
15171526
/// Attempts to get mutable references to `N` values in the map at once, without validating that
15181527
/// the values are unique.
15191528
///
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.
1529+
/// Returns an array of length `N` with the results of each query. `None` will be used if
1530+
/// the key is missing.
15221531
///
15231532
/// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`).
15241533
///
@@ -1540,29 +1549,31 @@ where
15401549
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
15411550
/// libraries.insert("Library of Congress".to_string(), 1800);
15421551
///
1543-
/// let got = libraries.get_many_mut([
1552+
/// // SAFETY: The keys do not overlap.
1553+
/// let got = unsafe { libraries.get_many_unchecked_mut([
15441554
/// "Athenæum",
15451555
/// "Library of Congress",
1546-
/// ]);
1556+
/// ]) };
15471557
/// assert_eq!(
15481558
/// got,
1549-
/// Some([
1550-
/// &mut 1807,
1551-
/// &mut 1800,
1552-
/// ]),
1559+
/// [
1560+
/// Some(&mut 1807),
1561+
/// Some(&mut 1800),
1562+
/// ],
15531563
/// );
15541564
///
1555-
/// // Missing keys result in None
1556-
/// let got = libraries.get_many_mut([
1565+
/// // SAFETY: The keys do not overlap.
1566+
/// let got = unsafe { libraries.get_many_unchecked_mut([
15571567
/// "Athenæum",
15581568
/// "New York Public Library",
1559-
/// ]);
1560-
/// assert_eq!(got, None);
1569+
/// ]) };
1570+
/// // Missing keys result in None
1571+
/// assert_eq!(got, [Some(&mut 1807), None]);
15611572
/// ```
15621573
pub unsafe fn get_many_unchecked_mut<Q, const N: usize>(
15631574
&mut self,
15641575
ks: [&Q; N],
1565-
) -> Option<[&'_ mut V; N]>
1576+
) -> [Option<&'_ mut V>; N]
15661577
where
15671578
Q: Hash + Equivalent<K> + ?Sized,
15681579
{
@@ -1574,8 +1585,11 @@ where
15741585
/// references to the corresponding keys.
15751586
///
15761587
/// 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.
1588+
/// mutable reference will be returned to any value. `None` will be used if the key is missing.
1589+
///
1590+
/// # Panics
1591+
///
1592+
/// Panics if some keys are overlapping.
15791593
///
15801594
/// # Examples
15811595
///
@@ -1594,30 +1608,37 @@ where
15941608
/// ]);
15951609
/// assert_eq!(
15961610
/// got,
1597-
/// Some([
1598-
/// (&"Bodleian Library".to_string(), &mut 1602),
1599-
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
1600-
/// ]),
1611+
/// [
1612+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1613+
/// Some((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
1614+
/// ],
16011615
/// );
16021616
/// // Missing keys result in None
16031617
/// let got = libraries.get_many_key_value_mut([
16041618
/// "Bodleian Library",
16051619
/// "Gewandhaus",
16061620
/// ]);
1607-
/// assert_eq!(got, None);
1621+
/// assert_eq!(got, [Some((&"Bodleian Library".to_string(), &mut 1602)), None]);
1622+
/// ```
1623+
///
1624+
/// ```should_panic
1625+
/// use hashbrown::HashMap;
1626+
///
1627+
/// let mut libraries = HashMap::new();
1628+
/// libraries.insert("Bodleian Library".to_string(), 1602);
1629+
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
16081630
///
1609-
/// // Duplicate keys result in None
1631+
/// // Duplicate keys result in panic!
16101632
/// let got = libraries.get_many_key_value_mut([
16111633
/// "Bodleian Library",
16121634
/// "Herzogin-Anna-Amalia-Bibliothek",
16131635
/// "Herzogin-Anna-Amalia-Bibliothek",
16141636
/// ]);
1615-
/// assert_eq!(got, None);
16161637
/// ```
16171638
pub fn get_many_key_value_mut<Q, const N: usize>(
16181639
&mut self,
16191640
ks: [&Q; N],
1620-
) -> Option<[(&'_ K, &'_ mut V); N]>
1641+
) -> [Option<(&'_ K, &'_ mut V)>; N]
16211642
where
16221643
Q: Hash + Equivalent<K> + ?Sized,
16231644
{
@@ -1657,30 +1678,36 @@ where
16571678
/// ]);
16581679
/// assert_eq!(
16591680
/// got,
1660-
/// Some([
1661-
/// (&"Bodleian Library".to_string(), &mut 1602),
1662-
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
1663-
/// ]),
1681+
/// [
1682+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1683+
/// Some((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
1684+
/// ],
16641685
/// );
16651686
/// // Missing keys result in None
16661687
/// let got = libraries.get_many_key_value_mut([
16671688
/// "Bodleian Library",
16681689
/// "Gewandhaus",
16691690
/// ]);
1670-
/// assert_eq!(got, None);
1691+
/// assert_eq!(
1692+
/// got,
1693+
/// [
1694+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1695+
/// None,
1696+
/// ],
1697+
/// );
16711698
/// ```
16721699
pub unsafe fn get_many_key_value_unchecked_mut<Q, const N: usize>(
16731700
&mut self,
16741701
ks: [&Q; N],
1675-
) -> Option<[(&'_ K, &'_ mut V); N]>
1702+
) -> [Option<(&'_ K, &'_ mut V)>; N]
16761703
where
16771704
Q: Hash + Equivalent<K> + ?Sized,
16781705
{
16791706
self.get_many_unchecked_mut_inner(ks)
16801707
.map(|res| res.map(|(k, v)| (&*k, v)))
16811708
}
16821709

1683-
fn get_many_mut_inner<Q, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut (K, V); N]>
1710+
fn get_many_mut_inner<Q, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut (K, V)>; N]
16841711
where
16851712
Q: Hash + Equivalent<K> + ?Sized,
16861713
{
@@ -1692,7 +1719,7 @@ where
16921719
unsafe fn get_many_unchecked_mut_inner<Q, const N: usize>(
16931720
&mut self,
16941721
ks: [&Q; N],
1695-
) -> Option<[&'_ mut (K, V); N]>
1722+
) -> [Option<&'_ mut (K, V)>; N]
16961723
where
16971724
Q: Hash + Equivalent<K> + ?Sized,
16981725
{
@@ -5937,33 +5964,39 @@ mod test_map {
59375964
}
59385965

59395966
#[test]
5940-
fn test_get_each_mut() {
5967+
fn test_get_many_mut() {
59415968
let mut map = HashMap::new();
59425969
map.insert("foo".to_owned(), 0);
59435970
map.insert("bar".to_owned(), 10);
59445971
map.insert("baz".to_owned(), 20);
59455972
map.insert("qux".to_owned(), 30);
59465973

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

59505977
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);
5978+
assert_eq!(xs, [Some(&mut 0), None]);
59555979

59565980
let ys = map.get_many_key_value_mut(["bar", "baz"]);
59575981
assert_eq!(
59585982
ys,
5959-
Some([(&"bar".to_owned(), &mut 10), (&"baz".to_owned(), &mut 20),]),
5983+
[
5984+
Some((&"bar".to_owned(), &mut 10)),
5985+
Some((&"baz".to_owned(), &mut 20))
5986+
],
59605987
);
59615988

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

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

59696002
#[test]

src/raw/mod.rs

Lines changed: 21 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,13 @@ impl<T> Bucket<T> {
484484
}
485485
}
486486

487+
/// Acquires the underlying non-null pointer `*mut T` to `data`.
488+
#[inline]
489+
fn as_non_null(&self) -> NonNull<T> {
490+
// SAFETY: `self.ptr` is already a `NonNull`
491+
unsafe { NonNull::new_unchecked(self.as_ptr()) }
492+
}
493+
487494
/// Create a new [`Bucket`] that is offset from the `self` by the given
488495
/// `offset`. The pointer calculation is performed by calculating the
489496
/// offset from `self` pointer (convenience for `self.ptr.as_ptr().sub(offset)`).
@@ -1291,48 +1298,46 @@ impl<T, A: Allocator> RawTable<T, A> {
12911298
&mut self,
12921299
hashes: [u64; N],
12931300
eq: impl FnMut(usize, &T) -> bool,
1294-
) -> Option<[&'_ mut T; N]> {
1301+
) -> [Option<&'_ mut T>; N] {
12951302
unsafe {
1296-
let ptrs = self.get_many_mut_pointers(hashes, eq)?;
1303+
let ptrs = self.get_many_mut_pointers(hashes, eq);
12971304

1298-
for (i, &cur) in ptrs.iter().enumerate() {
1299-
if ptrs[..i].iter().any(|&prev| ptr::eq::<T>(prev, cur)) {
1300-
return None;
1305+
for (i, cur) in ptrs.iter().enumerate() {
1306+
if cur.is_some() && ptrs[..i].contains(&cur) {
1307+
panic!("duplicate keys found");
13011308
}
13021309
}
13031310
// All bucket are distinct from all previous buckets so we're clear to return the result
13041311
// of the lookup.
13051312

1306-
// TODO use `MaybeUninit::array_assume_init` here instead once that's stable.
1307-
Some(mem::transmute_copy(&ptrs))
1313+
ptrs.map(|ptr| ptr.map(|mut ptr| ptr.as_mut()))
13081314
}
13091315
}
13101316

13111317
pub unsafe fn get_many_unchecked_mut<const N: usize>(
13121318
&mut self,
13131319
hashes: [u64; N],
13141320
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))
1321+
) -> [Option<&'_ mut T>; N] {
1322+
let ptrs = self.get_many_mut_pointers(hashes, eq);
1323+
ptrs.map(|ptr| ptr.map(|mut ptr| ptr.as_mut()))
13181324
}
13191325

13201326
unsafe fn get_many_mut_pointers<const N: usize>(
13211327
&mut self,
13221328
hashes: [u64; N],
13231329
mut eq: impl FnMut(usize, &T) -> bool,
1324-
) -> Option<[*mut T; N]> {
1330+
) -> [Option<NonNull<T>>; N] {
13251331
// TODO use `MaybeUninit::uninit_array` here instead once that's stable.
1326-
let mut outs: MaybeUninit<[*mut T; N]> = MaybeUninit::uninit();
1332+
let mut outs: MaybeUninit<[Option<NonNull<T>>; N]> = MaybeUninit::uninit();
13271333
let outs_ptr = outs.as_mut_ptr();
13281334

13291335
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();
1336+
let cur = self.find(hash, |k| eq(i, k)).map(|cur| cur.as_non_null());
1337+
*(*outs_ptr).get_unchecked_mut(i) = cur;
13321338
}
13331339

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

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

0 commit comments

Comments
 (0)