Skip to content

Commit 72f0146

Browse files
committed
Implement get_many_index_mut & and add docs
Avoid mutable aliasing by using a pointer to entries
1 parent 66108be commit 72f0146

File tree

1 file changed

+57
-20
lines changed

1 file changed

+57
-20
lines changed

src/map.rs

Lines changed: 57 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -477,37 +477,35 @@ where
477477
}
478478
}
479479

480+
/// Return the values for `N` keys. If any key is missing a value, or there
481+
/// are duplicate keys, `None` is returned.
482+
///
483+
/// # Examples
484+
///
485+
/// ```
486+
/// let mut map = indexmap::IndexMap::from([(1, 'a'), (3, 'b'), (2, 'c')]);
487+
/// assert_eq!(map.get_many_mut([&2, &1]), Some([&mut 'c', &mut 'a']));
488+
/// ```
480489
pub fn get_many_mut<'a, 'b, Q: ?Sized, const N: usize>(
481490
&'a mut self,
482491
keys: [&'b Q; N],
483492
) -> Option<[&'a mut V; N]>
484493
where
485494
Q: Hash + Equivalent<K>,
486495
{
496+
let len = self.len();
487497
let indices = keys.map(|key| self.get_index_of(key));
488-
if indices.iter().any(Option::is_none) {
489-
return None;
490-
}
491-
let indices = indices.map(Option::unwrap);
492498

493-
// SAFETY: Can't allow duplicate indices as we would return several mutable refs to the same data
494-
for i in 0..N {
495-
let idx = indices[i];
496-
if indices[i + 1..N].contains(&idx) {
497-
return None;
499+
// Handle out-of-bounds indices with panic as this is an internal error in get_index_of.
500+
for idx in indices {
501+
let idx = idx?;
502+
if idx >= len {
503+
panic!("Index is out of range! Got '{idx}' but length is '{len}'")
498504
}
499505
}
500-
501-
let entries = self.as_entries_mut();
502-
let out = indices.map(|i| {
503-
// SAFETY: OK to discard mutable borrow lifetime as each index is unique
504-
#[allow(unsafe_code)]
505-
unsafe {
506-
&mut *(&mut entries[i].value as *mut V)
507-
}
508-
});
509-
510-
Some(out)
506+
let indices = indices.map(Option::unwrap);
507+
let entries = self.get_many_index_mut(indices)?;
508+
Some(entries.map(|(_key, value)| value))
511509
}
512510

513511
/// Remove the key-value pair equivalent to `key` and return
@@ -817,6 +815,45 @@ impl<K, V, S> IndexMap<K, V, S> {
817815
self.as_entries_mut().get_mut(index).map(Bucket::ref_mut)
818816
}
819817

818+
/// Get an array of `N` key-value pairs by `N` indices
819+
///
820+
/// Valid indices are *0 <= index < self.len()* and each index needs to be unique.
821+
///
822+
/// Computes in **O(1)** time.
823+
///
824+
/// # Examples
825+
///
826+
/// ```
827+
/// let mut map = indexmap::IndexMap::from([(1, 'a'), (3, 'b'), (2, 'c')]);
828+
/// assert_eq!(map.get_many_index_mut([2, 0]), Some([(&2, &mut 'c'), (&1, &mut 'a')]));
829+
/// ```
830+
pub fn get_many_index_mut<const N: usize>(
831+
&mut self,
832+
indices: [usize; N],
833+
) -> Option<[(&K, &mut V); N]> {
834+
// SAFETY: Can't allow duplicate indices as we would return several mutable refs to the same data.
835+
// Additionally, handle out-of-bounds indices (internal error in get_index_of) with panic.
836+
let len = self.len();
837+
for i in 0..N {
838+
let idx = indices[i];
839+
if idx >= len || indices[i + 1..N].contains(&idx) {
840+
return None;
841+
}
842+
}
843+
844+
let entries_ptr = self.as_entries_mut().as_mut_ptr();
845+
let out = indices.map(|i| {
846+
// SAFETY: The base pointer is valid as it comes from a slice and the deref is always
847+
// in-bounds as we've already checked the indices above.
848+
#[allow(unsafe_code)]
849+
unsafe {
850+
(*(entries_ptr.add(i))).ref_mut()
851+
}
852+
});
853+
854+
Some(out)
855+
}
856+
820857
/// Returns a slice of key-value pairs in the given range of indices.
821858
///
822859
/// Valid indices are *0 <= index < self.len()*

0 commit comments

Comments
 (0)