Skip to content

Commit c1ee6ee

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. And address a potential future UB by only using raw pointers rust-lang/unsafe-code-guidelines#346 a
1 parent 7cf51ea commit c1ee6ee

File tree

3 files changed

+157
-91
lines changed

3 files changed

+157
-91
lines changed

src/map.rs

Lines changed: 98 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 any keys are overlapping.
14721475
///
14731476
/// # Examples
14741477
///
@@ -1481,33 +1484,46 @@ where
14811484
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
14821485
/// libraries.insert("Library of Congress".to_string(), 1800);
14831486
///
1487+
/// // Get Athenæum and Bodleian Library
1488+
/// let [Some(a), Some(b)] = libraries.get_many_mut([
1489+
/// "Athenæum",
1490+
/// "Bodleian Library",
1491+
/// ]) else { panic!() };
1492+
///
1493+
/// // Assert values of Athenæum and Library of Congress
14841494
/// let got = libraries.get_many_mut([
14851495
/// "Athenæum",
14861496
/// "Library of Congress",
14871497
/// ]);
14881498
/// assert_eq!(
14891499
/// got,
1490-
/// Some([
1491-
/// &mut 1807,
1492-
/// &mut 1800,
1493-
/// ]),
1500+
/// [
1501+
/// Some(&mut 1807),
1502+
/// Some(&mut 1800),
1503+
/// ],
14941504
/// );
14951505
///
14961506
/// // Missing keys result in None
14971507
/// let got = libraries.get_many_mut([
1498-
/// "Athenæum",
1508+
/// "Athenæum1",
14991509
/// "New York Public Library",
15001510
/// ]);
1501-
/// assert_eq!(got, None);
1511+
/// assert_eq!(got, [None, None]);
1512+
/// ```
1513+
///
1514+
/// ```should_panic
1515+
/// use hashbrown::HashMap;
1516+
///
1517+
/// let mut libraries = HashMap::new();
1518+
/// libraries.insert("Athenæum".to_string(), 1807);
15021519
///
1503-
/// // Duplicate keys result in None
1520+
/// // Duplicate keys panic!
15041521
/// let got = libraries.get_many_mut([
15051522
/// "Athenæum",
15061523
/// "Athenæum",
15071524
/// ]);
1508-
/// assert_eq!(got, None);
15091525
/// ```
1510-
pub fn get_many_mut<Q, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut V; N]>
1526+
pub fn get_many_mut<Q, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N]
15111527
where
15121528
Q: Hash + Equivalent<K> + ?Sized,
15131529
{
@@ -1517,8 +1533,8 @@ where
15171533
/// Attempts to get mutable references to `N` values in the map at once, without validating that
15181534
/// the values are unique.
15191535
///
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.
1536+
/// Returns an array of length `N` with the results of each query. `None` will be used if
1537+
/// the key is missing.
15221538
///
15231539
/// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`).
15241540
///
@@ -1540,29 +1556,37 @@ where
15401556
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
15411557
/// libraries.insert("Library of Congress".to_string(), 1800);
15421558
///
1543-
/// let got = libraries.get_many_mut([
1559+
/// // SAFETY: The keys do not overlap.
1560+
/// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([
1561+
/// "Athenæum",
1562+
/// "Bodleian Library",
1563+
/// ]) }) else { panic!() };
1564+
///
1565+
/// // SAFETY: The keys do not overlap.
1566+
/// let got = unsafe { libraries.get_many_unchecked_mut([
15441567
/// "Athenæum",
15451568
/// "Library of Congress",
1546-
/// ]);
1569+
/// ]) };
15471570
/// assert_eq!(
15481571
/// got,
1549-
/// Some([
1550-
/// &mut 1807,
1551-
/// &mut 1800,
1552-
/// ]),
1572+
/// [
1573+
/// Some(&mut 1807),
1574+
/// Some(&mut 1800),
1575+
/// ],
15531576
/// );
15541577
///
1555-
/// // Missing keys result in None
1556-
/// let got = libraries.get_many_mut([
1578+
/// // SAFETY: The keys do not overlap.
1579+
/// let got = unsafe { libraries.get_many_unchecked_mut([
15571580
/// "Athenæum",
15581581
/// "New York Public Library",
1559-
/// ]);
1560-
/// assert_eq!(got, None);
1582+
/// ]) };
1583+
/// // Missing keys result in None
1584+
/// assert_eq!(got, [Some(&mut 1807), None]);
15611585
/// ```
15621586
pub unsafe fn get_many_unchecked_mut<Q, const N: usize>(
15631587
&mut self,
15641588
ks: [&Q; N],
1565-
) -> Option<[&'_ mut V; N]>
1589+
) -> [Option<&'_ mut V>; N]
15661590
where
15671591
Q: Hash + Equivalent<K> + ?Sized,
15681592
{
@@ -1574,8 +1598,11 @@ where
15741598
/// references to the corresponding keys.
15751599
///
15761600
/// 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.
1601+
/// mutable reference will be returned to any value. `None` will be used if the key is missing.
1602+
///
1603+
/// # Panics
1604+
///
1605+
/// Panics if any keys are overlapping.
15791606
///
15801607
/// # Examples
15811608
///
@@ -1594,30 +1621,37 @@ where
15941621
/// ]);
15951622
/// assert_eq!(
15961623
/// got,
1597-
/// Some([
1598-
/// (&"Bodleian Library".to_string(), &mut 1602),
1599-
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
1600-
/// ]),
1624+
/// [
1625+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1626+
/// Some((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
1627+
/// ],
16011628
/// );
16021629
/// // Missing keys result in None
16031630
/// let got = libraries.get_many_key_value_mut([
16041631
/// "Bodleian Library",
16051632
/// "Gewandhaus",
16061633
/// ]);
1607-
/// assert_eq!(got, None);
1634+
/// assert_eq!(got, [Some((&"Bodleian Library".to_string(), &mut 1602)), None]);
1635+
/// ```
16081636
///
1609-
/// // Duplicate keys result in None
1637+
/// ```should_panic
1638+
/// use hashbrown::HashMap;
1639+
///
1640+
/// let mut libraries = HashMap::new();
1641+
/// libraries.insert("Bodleian Library".to_string(), 1602);
1642+
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
1643+
///
1644+
/// // Duplicate keys result in panic!
16101645
/// let got = libraries.get_many_key_value_mut([
16111646
/// "Bodleian Library",
16121647
/// "Herzogin-Anna-Amalia-Bibliothek",
16131648
/// "Herzogin-Anna-Amalia-Bibliothek",
16141649
/// ]);
1615-
/// assert_eq!(got, None);
16161650
/// ```
16171651
pub fn get_many_key_value_mut<Q, const N: usize>(
16181652
&mut self,
16191653
ks: [&Q; N],
1620-
) -> Option<[(&'_ K, &'_ mut V); N]>
1654+
) -> [Option<(&'_ K, &'_ mut V)>; N]
16211655
where
16221656
Q: Hash + Equivalent<K> + ?Sized,
16231657
{
@@ -1657,30 +1691,36 @@ where
16571691
/// ]);
16581692
/// assert_eq!(
16591693
/// got,
1660-
/// Some([
1661-
/// (&"Bodleian Library".to_string(), &mut 1602),
1662-
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
1663-
/// ]),
1694+
/// [
1695+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1696+
/// Some((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
1697+
/// ],
16641698
/// );
16651699
/// // Missing keys result in None
16661700
/// let got = libraries.get_many_key_value_mut([
16671701
/// "Bodleian Library",
16681702
/// "Gewandhaus",
16691703
/// ]);
1670-
/// assert_eq!(got, None);
1704+
/// assert_eq!(
1705+
/// got,
1706+
/// [
1707+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1708+
/// None,
1709+
/// ],
1710+
/// );
16711711
/// ```
16721712
pub unsafe fn get_many_key_value_unchecked_mut<Q, const N: usize>(
16731713
&mut self,
16741714
ks: [&Q; N],
1675-
) -> Option<[(&'_ K, &'_ mut V); N]>
1715+
) -> [Option<(&'_ K, &'_ mut V)>; N]
16761716
where
16771717
Q: Hash + Equivalent<K> + ?Sized,
16781718
{
16791719
self.get_many_unchecked_mut_inner(ks)
16801720
.map(|res| res.map(|(k, v)| (&*k, v)))
16811721
}
16821722

1683-
fn get_many_mut_inner<Q, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut (K, V); N]>
1723+
fn get_many_mut_inner<Q, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut (K, V)>; N]
16841724
where
16851725
Q: Hash + Equivalent<K> + ?Sized,
16861726
{
@@ -1692,7 +1732,7 @@ where
16921732
unsafe fn get_many_unchecked_mut_inner<Q, const N: usize>(
16931733
&mut self,
16941734
ks: [&Q; N],
1695-
) -> Option<[&'_ mut (K, V); N]>
1735+
) -> [Option<&'_ mut (K, V)>; N]
16961736
where
16971737
Q: Hash + Equivalent<K> + ?Sized,
16981738
{
@@ -5937,33 +5977,39 @@ mod test_map {
59375977
}
59385978

59395979
#[test]
5940-
fn test_get_each_mut() {
5980+
fn test_get_many_mut() {
59415981
let mut map = HashMap::new();
59425982
map.insert("foo".to_owned(), 0);
59435983
map.insert("bar".to_owned(), 10);
59445984
map.insert("baz".to_owned(), 20);
59455985
map.insert("qux".to_owned(), 30);
59465986

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

59505990
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);
5991+
assert_eq!(xs, [Some(&mut 0), None]);
59555992

59565993
let ys = map.get_many_key_value_mut(["bar", "baz"]);
59575994
assert_eq!(
59585995
ys,
5959-
Some([(&"bar".to_owned(), &mut 10), (&"baz".to_owned(), &mut 20),]),
5996+
[
5997+
Some((&"bar".to_owned(), &mut 10)),
5998+
Some((&"baz".to_owned(), &mut 20))
5999+
],
59606000
);
59616001

59626002
let ys = map.get_many_key_value_mut(["bar", "dip"]);
5963-
assert_eq!(ys, None);
6003+
assert_eq!(ys, [Some((&"bar".to_string(), &mut 10)), None]);
6004+
}
6005+
6006+
#[test]
6007+
#[should_panic = "duplicate keys found"]
6008+
fn test_get_many_mut_duplicate() {
6009+
let mut map = HashMap::new();
6010+
map.insert("foo".to_owned(), 0);
59646011

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

59696015
#[test]

src/raw/mod.rs

Lines changed: 22 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
use crate::alloc::alloc::{handle_alloc_error, Layout};
22
use crate::scopeguard::{guard, ScopeGuard};
33
use crate::TryReserveError;
4+
use core::array;
45
use core::iter::FusedIterator;
56
use core::marker::PhantomData;
67
use core::mem;
7-
use core::mem::MaybeUninit;
88
use core::ptr::NonNull;
99
use core::{hint, ptr};
1010

@@ -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,40 @@ 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]> {
1325-
// TODO use `MaybeUninit::uninit_array` here instead once that's stable.
1326-
let mut outs: MaybeUninit<[*mut T; N]> = MaybeUninit::uninit();
1327-
let outs_ptr = outs.as_mut_ptr();
1328-
1329-
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();
1332-
}
1333-
1334-
// TODO use `MaybeUninit::array_assume_init` here instead once that's stable.
1335-
Some(outs.assume_init())
1330+
) -> [Option<NonNull<T>>; N] {
1331+
array::from_fn(|i| {
1332+
self.find(hashes[i], |k| eq(i, k))
1333+
.map(|cur| cur.as_non_null())
1334+
})
13361335
}
13371336

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

0 commit comments

Comments
 (0)