Skip to content

Commit edd22e1

Browse files
committed
Auto merge of #562 - Urgau:new-get-many-mut, r=Amanieu
Change signature of `get_many_mut` APIs This PR changes the signature and contract of the `get_many_mut` APIs by 1. panicking on overlapping keys 2. returning an array of Option rather than an Option of array. This was asked by T-libs-api in rust-lang/rust#97601 (comment) regarding the corresponding std `HashMap` functions.
2 parents 7cf51ea + d50e3b2 commit edd22e1

File tree

3 files changed

+162
-90
lines changed

3 files changed

+162
-90
lines changed

src/map.rs

Lines changed: 103 additions & 51 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,52 @@ 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([
14981508
/// "Athenæum",
14991509
/// "New York Public Library",
15001510
/// ]);
1501-
/// assert_eq!(got, None);
1511+
/// assert_eq!(
1512+
/// got,
1513+
/// [
1514+
/// Some(&mut 1807),
1515+
/// None
1516+
/// ]
1517+
/// );
1518+
/// ```
1519+
///
1520+
/// ```should_panic
1521+
/// use hashbrown::HashMap;
15021522
///
1503-
/// // Duplicate keys result in None
1523+
/// let mut libraries = HashMap::new();
1524+
/// libraries.insert("Athenæum".to_string(), 1807);
1525+
///
1526+
/// // Duplicate keys panic!
15041527
/// let got = libraries.get_many_mut([
15051528
/// "Athenæum",
15061529
/// "Athenæum",
15071530
/// ]);
1508-
/// assert_eq!(got, None);
15091531
/// ```
1510-
pub fn get_many_mut<Q, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut V; N]>
1532+
pub fn get_many_mut<Q, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut V>; N]
15111533
where
15121534
Q: Hash + Equivalent<K> + ?Sized,
15131535
{
@@ -1517,8 +1539,8 @@ where
15171539
/// Attempts to get mutable references to `N` values in the map at once, without validating that
15181540
/// the values are unique.
15191541
///
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.
1542+
/// Returns an array of length `N` with the results of each query. `None` will be used if
1543+
/// the key is missing.
15221544
///
15231545
/// For a safe alternative see [`get_many_mut`](`HashMap::get_many_mut`).
15241546
///
@@ -1540,29 +1562,37 @@ where
15401562
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
15411563
/// libraries.insert("Library of Congress".to_string(), 1800);
15421564
///
1543-
/// let got = libraries.get_many_mut([
1565+
/// // SAFETY: The keys do not overlap.
1566+
/// let [Some(a), Some(b)] = (unsafe { libraries.get_many_unchecked_mut([
1567+
/// "Athenæum",
1568+
/// "Bodleian Library",
1569+
/// ]) }) else { panic!() };
1570+
///
1571+
/// // SAFETY: The keys do not overlap.
1572+
/// let got = unsafe { libraries.get_many_unchecked_mut([
15441573
/// "Athenæum",
15451574
/// "Library of Congress",
1546-
/// ]);
1575+
/// ]) };
15471576
/// assert_eq!(
15481577
/// got,
1549-
/// Some([
1550-
/// &mut 1807,
1551-
/// &mut 1800,
1552-
/// ]),
1578+
/// [
1579+
/// Some(&mut 1807),
1580+
/// Some(&mut 1800),
1581+
/// ],
15531582
/// );
15541583
///
1555-
/// // Missing keys result in None
1556-
/// let got = libraries.get_many_mut([
1584+
/// // SAFETY: The keys do not overlap.
1585+
/// let got = unsafe { libraries.get_many_unchecked_mut([
15571586
/// "Athenæum",
15581587
/// "New York Public Library",
1559-
/// ]);
1560-
/// assert_eq!(got, None);
1588+
/// ]) };
1589+
/// // Missing keys result in None
1590+
/// assert_eq!(got, [Some(&mut 1807), None]);
15611591
/// ```
15621592
pub unsafe fn get_many_unchecked_mut<Q, const N: usize>(
15631593
&mut self,
15641594
ks: [&Q; N],
1565-
) -> Option<[&'_ mut V; N]>
1595+
) -> [Option<&'_ mut V>; N]
15661596
where
15671597
Q: Hash + Equivalent<K> + ?Sized,
15681598
{
@@ -1574,8 +1604,11 @@ where
15741604
/// references to the corresponding keys.
15751605
///
15761606
/// 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.
1607+
/// mutable reference will be returned to any value. `None` will be used if the key is missing.
1608+
///
1609+
/// # Panics
1610+
///
1611+
/// Panics if any keys are overlapping.
15791612
///
15801613
/// # Examples
15811614
///
@@ -1594,30 +1627,37 @@ where
15941627
/// ]);
15951628
/// assert_eq!(
15961629
/// got,
1597-
/// Some([
1598-
/// (&"Bodleian Library".to_string(), &mut 1602),
1599-
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
1600-
/// ]),
1630+
/// [
1631+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1632+
/// Some((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
1633+
/// ],
16011634
/// );
16021635
/// // Missing keys result in None
16031636
/// let got = libraries.get_many_key_value_mut([
16041637
/// "Bodleian Library",
16051638
/// "Gewandhaus",
16061639
/// ]);
1607-
/// assert_eq!(got, None);
1640+
/// assert_eq!(got, [Some((&"Bodleian Library".to_string(), &mut 1602)), None]);
1641+
/// ```
1642+
///
1643+
/// ```should_panic
1644+
/// use hashbrown::HashMap;
1645+
///
1646+
/// let mut libraries = HashMap::new();
1647+
/// libraries.insert("Bodleian Library".to_string(), 1602);
1648+
/// libraries.insert("Herzogin-Anna-Amalia-Bibliothek".to_string(), 1691);
16081649
///
1609-
/// // Duplicate keys result in None
1650+
/// // Duplicate keys result in panic!
16101651
/// let got = libraries.get_many_key_value_mut([
16111652
/// "Bodleian Library",
16121653
/// "Herzogin-Anna-Amalia-Bibliothek",
16131654
/// "Herzogin-Anna-Amalia-Bibliothek",
16141655
/// ]);
1615-
/// assert_eq!(got, None);
16161656
/// ```
16171657
pub fn get_many_key_value_mut<Q, const N: usize>(
16181658
&mut self,
16191659
ks: [&Q; N],
1620-
) -> Option<[(&'_ K, &'_ mut V); N]>
1660+
) -> [Option<(&'_ K, &'_ mut V)>; N]
16211661
where
16221662
Q: Hash + Equivalent<K> + ?Sized,
16231663
{
@@ -1657,30 +1697,36 @@ where
16571697
/// ]);
16581698
/// assert_eq!(
16591699
/// got,
1660-
/// Some([
1661-
/// (&"Bodleian Library".to_string(), &mut 1602),
1662-
/// (&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691),
1663-
/// ]),
1700+
/// [
1701+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1702+
/// Some((&"Herzogin-Anna-Amalia-Bibliothek".to_string(), &mut 1691)),
1703+
/// ],
16641704
/// );
16651705
/// // Missing keys result in None
16661706
/// let got = libraries.get_many_key_value_mut([
16671707
/// "Bodleian Library",
16681708
/// "Gewandhaus",
16691709
/// ]);
1670-
/// assert_eq!(got, None);
1710+
/// assert_eq!(
1711+
/// got,
1712+
/// [
1713+
/// Some((&"Bodleian Library".to_string(), &mut 1602)),
1714+
/// None,
1715+
/// ],
1716+
/// );
16711717
/// ```
16721718
pub unsafe fn get_many_key_value_unchecked_mut<Q, const N: usize>(
16731719
&mut self,
16741720
ks: [&Q; N],
1675-
) -> Option<[(&'_ K, &'_ mut V); N]>
1721+
) -> [Option<(&'_ K, &'_ mut V)>; N]
16761722
where
16771723
Q: Hash + Equivalent<K> + ?Sized,
16781724
{
16791725
self.get_many_unchecked_mut_inner(ks)
16801726
.map(|res| res.map(|(k, v)| (&*k, v)))
16811727
}
16821728

1683-
fn get_many_mut_inner<Q, const N: usize>(&mut self, ks: [&Q; N]) -> Option<[&'_ mut (K, V); N]>
1729+
fn get_many_mut_inner<Q, const N: usize>(&mut self, ks: [&Q; N]) -> [Option<&'_ mut (K, V)>; N]
16841730
where
16851731
Q: Hash + Equivalent<K> + ?Sized,
16861732
{
@@ -1692,7 +1738,7 @@ where
16921738
unsafe fn get_many_unchecked_mut_inner<Q, const N: usize>(
16931739
&mut self,
16941740
ks: [&Q; N],
1695-
) -> Option<[&'_ mut (K, V); N]>
1741+
) -> [Option<&'_ mut (K, V)>; N]
16961742
where
16971743
Q: Hash + Equivalent<K> + ?Sized,
16981744
{
@@ -5937,33 +5983,39 @@ mod test_map {
59375983
}
59385984

59395985
#[test]
5940-
fn test_get_each_mut() {
5986+
fn test_get_many_mut() {
59415987
let mut map = HashMap::new();
59425988
map.insert("foo".to_owned(), 0);
59435989
map.insert("bar".to_owned(), 10);
59445990
map.insert("baz".to_owned(), 20);
59455991
map.insert("qux".to_owned(), 30);
59465992

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

59505996
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);
5997+
assert_eq!(xs, [Some(&mut 0), None]);
59555998

59565999
let ys = map.get_many_key_value_mut(["bar", "baz"]);
59576000
assert_eq!(
59586001
ys,
5959-
Some([(&"bar".to_owned(), &mut 10), (&"baz".to_owned(), &mut 20),]),
6002+
[
6003+
Some((&"bar".to_owned(), &mut 10)),
6004+
Some((&"baz".to_owned(), &mut 20))
6005+
],
59606006
);
59616007

59626008
let ys = map.get_many_key_value_mut(["bar", "dip"]);
5963-
assert_eq!(ys, None);
6009+
assert_eq!(ys, [Some((&"bar".to_string(), &mut 10)), None]);
6010+
}
6011+
6012+
#[test]
6013+
#[should_panic = "duplicate keys found"]
6014+
fn test_get_many_mut_duplicate() {
6015+
let mut map = HashMap::new();
6016+
map.insert("foo".to_owned(), 0);
59646017

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

59696021
#[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)