diff --git a/Cargo.toml b/Cargo.toml index 58b39865..bdd94be5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "indexmap" edition = "2021" -version = "2.2.6" +version = "2.3.0" documentation = "https://docs.rs/indexmap/" repository = "https://github.com/indexmap-rs/indexmap" license = "Apache-2.0 OR MIT" diff --git a/RELEASES.md b/RELEASES.md index 8c033db0..3b30af76 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,11 @@ # Releases +## 2.3.0 + +- Added trait `MutableEntryKey` for opt-in mutable access to map entry keys. +- Added method `MutableKeys::iter_mut2` for opt-in mutable iteration of map + keys and values. + ## 2.2.6 - Added trait `MutableValues` for opt-in mutable access to set values. diff --git a/src/map.rs b/src/map.rs index 82824c90..ea7e1b48 100644 --- a/src/map.rs +++ b/src/map.rs @@ -16,8 +16,9 @@ mod tests; pub use self::core::raw_entry_v1::{self, RawEntryApiV1}; pub use self::core::{Entry, IndexedEntry, OccupiedEntry, VacantEntry}; pub use self::iter::{ - Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Splice, Values, ValuesMut, + Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, IterMut2, Keys, Splice, Values, ValuesMut, }; +pub use self::mutable::MutableEntryKey; pub use self::mutable::MutableKeys; pub use self::slice::Slice; diff --git a/src/map/core/entry.rs b/src/map/core/entry.rs index 6c310707..ab45ecc1 100644 --- a/src/map/core/entry.rs +++ b/src/map/core/entry.rs @@ -144,6 +144,10 @@ impl<'a, K, V> OccupiedEntry<'a, K, V> { &self.raw.bucket().key } + pub(crate) fn key_mut(&mut self) -> &mut K { + &mut self.raw.bucket_mut().key + } + /// Gets a reference to the entry's value in the map. pub fn get(&self) -> &V { &self.raw.bucket().value @@ -297,6 +301,10 @@ impl<'a, K, V> VacantEntry<'a, K, V> { &self.key } + pub(crate) fn key_mut(&mut self) -> &mut K { + &mut self.key + } + /// Takes ownership of the key, leaving the entry vacant. pub fn into_key(self) -> K { self.key @@ -373,6 +381,10 @@ impl<'a, K, V> IndexedEntry<'a, K, V> { &self.map.entries[self.index].key } + pub(crate) fn key_mut(&mut self) -> &mut K { + &mut self.map.entries[self.index].key + } + /// Gets a reference to the entry's value in the map. pub fn get(&self) -> &V { &self.map.entries[self.index].value diff --git a/src/map/iter.rs b/src/map/iter.rs index 1ec3703c..9bbf9d9f 100644 --- a/src/map/iter.rs +++ b/src/map/iter.rs @@ -156,6 +156,67 @@ impl Default for IterMut<'_, K, V> { } } +/// A mutable iterator over the entries of an [`IndexMap`]. +/// +/// This `struct` is created by the [`MutableKeys::iter_mut2`][super::MutableKeys::iter_mut2] method. +/// See its documentation for more. +pub struct IterMut2<'a, K, V> { + iter: slice::IterMut<'a, Bucket>, +} + +impl<'a, K, V> IterMut2<'a, K, V> { + pub(super) fn new(entries: &'a mut [Bucket]) -> Self { + Self { + iter: entries.iter_mut(), + } + } + + /// Returns a slice of the remaining entries in the iterator. + pub fn as_slice(&self) -> &Slice { + Slice::from_slice(self.iter.as_slice()) + } + + /// Returns a mutable slice of the remaining entries in the iterator. + /// + /// To avoid creating `&mut` references that alias, this is forced to consume the iterator. + pub fn into_slice(self) -> &'a mut Slice { + Slice::from_mut_slice(self.iter.into_slice()) + } +} + +impl<'a, K, V> Iterator for IterMut2<'a, K, V> { + type Item = (&'a mut K, &'a mut V); + + iterator_methods!(Bucket::muts); +} + +impl DoubleEndedIterator for IterMut2<'_, K, V> { + double_ended_iterator_methods!(Bucket::muts); +} + +impl ExactSizeIterator for IterMut2<'_, K, V> { + fn len(&self) -> usize { + self.iter.len() + } +} + +impl FusedIterator for IterMut2<'_, K, V> {} + +impl fmt::Debug for IterMut2<'_, K, V> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let iter = self.iter.as_slice().iter().map(Bucket::refs); + f.debug_list().entries(iter).finish() + } +} + +impl Default for IterMut2<'_, K, V> { + fn default() -> Self { + Self { + iter: [].iter_mut(), + } + } +} + /// An owning iterator over the entries of an [`IndexMap`]. /// /// This `struct` is created by the [`IndexMap::into_iter`] method diff --git a/src/map/mutable.rs b/src/map/mutable.rs index 7df32594..c778971b 100644 --- a/src/map/mutable.rs +++ b/src/map/mutable.rs @@ -1,6 +1,9 @@ use core::hash::{BuildHasher, Hash}; -use super::{Bucket, Entries, Equivalent, IndexMap}; +use super::{ + Bucket, Entries, Entry, Equivalent, IndexMap, IndexedEntry, IterMut2, OccupiedEntry, + VacantEntry, +}; /// Opt-in mutable access to [`IndexMap`] keys. /// @@ -34,6 +37,9 @@ pub trait MutableKeys: private::Sealed { /// Computes in **O(1)** time. fn get_index_mut2(&mut self, index: usize) -> Option<(&mut Self::Key, &mut Self::Value)>; + /// Return an iterator over the key-value pairs of the map, in their order + fn iter_mut2(&mut self) -> IterMut2<'_, Self::Key, Self::Value>; + /// Scan through each key-value pair in the map and keep those where the /// closure `keep` returns `true`. /// @@ -72,6 +78,10 @@ where self.as_entries_mut().get_mut(index).map(Bucket::muts) } + fn iter_mut2(&mut self) -> IterMut2<'_, Self::Key, Self::Value> { + IterMut2::new(self.as_entries_mut()) + } + fn retain2(&mut self, keep: F) where F: FnMut(&mut K, &mut V) -> bool, @@ -80,8 +90,77 @@ where } } +/// Opt-in mutable access to [`Entry`] keys. +/// +/// These methods expose `&mut K`, mutable references to the key as it is stored +/// in the map. +/// You are allowed to modify the keys in the map **if the modification +/// does not change the key’s hash and equality**. +/// +/// If keys are modified erroneously, you can no longer look them up. +/// This is sound (memory safe) but a logical error hazard (just like +/// implementing `PartialEq`, `Eq`, or `Hash` incorrectly would be). +/// +/// `use` this trait to enable its methods for `Entry`. +/// +/// This trait is sealed and cannot be implemented for types outside this crate. +pub trait MutableEntryKey: private::Sealed { + type Key; + fn key_mut(&mut self) -> &mut Self::Key; +} + +/// Opt-in mutable access to [`Entry`] keys. +/// +/// See [`MutableEntryKey`] for more information. +impl MutableEntryKey for Entry<'_, K, V> { + type Key = K; + + /// Gets a mutable reference to the entry's key, either within the map if occupied, + /// or else the new key that was used to find the entry. + fn key_mut(&mut self) -> &mut Self::Key { + match self { + Entry::Occupied(e) => e.key_mut(), + Entry::Vacant(e) => e.key_mut(), + } + } +} + +/// Opt-in mutable access to [`OccupiedEntry`] keys. +/// +/// See [`MutableEntryKey`] for more information. +impl MutableEntryKey for OccupiedEntry<'_, K, V> { + type Key = K; + fn key_mut(&mut self) -> &mut Self::Key { + self.key_mut() + } +} + +/// Opt-in mutable access to [`VacantEntry`] keys. +/// +/// See [`MutableEntryKey`] for more information. +impl MutableEntryKey for VacantEntry<'_, K, V> { + type Key = K; + fn key_mut(&mut self) -> &mut Self::Key { + self.key_mut() + } +} + +/// Opt-in mutable access to [`IndexedEntry`] keys. +/// +/// See [`MutableEntryKey`] for more information. +impl MutableEntryKey for IndexedEntry<'_, K, V> { + type Key = K; + fn key_mut(&mut self) -> &mut Self::Key { + self.key_mut() + } +} + mod private { pub trait Sealed {} impl Sealed for super::IndexMap {} + impl Sealed for super::Entry<'_, K, V> {} + impl Sealed for super::OccupiedEntry<'_, K, V> {} + impl Sealed for super::VacantEntry<'_, K, V> {} + impl Sealed for super::IndexedEntry<'_, K, V> {} } diff --git a/src/map/tests.rs b/src/map/tests.rs index bba78ff5..49541181 100644 --- a/src/map/tests.rs +++ b/src/map/tests.rs @@ -497,6 +497,7 @@ fn iter_default() { } assert_default::>(); assert_default::>(); + assert_default::>(); assert_default::>(); assert_default::>(); assert_default::>();