diff --git a/Cargo.toml b/Cargo.toml index 73ea6be5..f82ac3b0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "indexmap" edition = "2021" -version = "2.2.5" +version = "2.2.6" 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 4e11c545..8c033db0 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,5 +1,9 @@ # Releases +## 2.2.6 + +- Added trait `MutableValues` for opt-in mutable access to set values. + ## 2.2.5 - Added optional `borsh` serialization support. diff --git a/src/borsh.rs b/src/borsh.rs index 7b4afdc4..c485bd52 100644 --- a/src/borsh.rs +++ b/src/borsh.rs @@ -3,7 +3,6 @@ use alloc::vec::Vec; use core::hash::BuildHasher; use core::hash::Hash; -use core::iter::ExactSizeIterator; use core::mem::size_of; use borsh::error::ERROR_ZST_FORBIDDEN; diff --git a/src/lib.rs b/src/lib.rs index d6d3ede9..7d88ffef 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -19,7 +19,7 @@ //! - The [`Equivalent`] trait, which offers more flexible equality definitions //! between borrowed and owned versions of keys. //! - The [`MutableKeys`][map::MutableKeys] trait, which gives opt-in mutable -//! access to hash map keys. +//! access to map keys, and [`MutableValues`][set::MutableValues] for sets. //! //! ### Feature Flags //! @@ -116,7 +116,6 @@ mod arbitrary; mod macros; #[cfg(feature = "borsh")] mod borsh; -mod mutable_keys; #[cfg(feature = "serde")] mod serde; mod util; diff --git a/src/map.rs b/src/map.rs index 46965a7c..82824c90 100644 --- a/src/map.rs +++ b/src/map.rs @@ -3,6 +3,7 @@ mod core; mod iter; +mod mutable; mod slice; #[cfg(feature = "serde")] @@ -17,8 +18,8 @@ pub use self::core::{Entry, IndexedEntry, OccupiedEntry, VacantEntry}; pub use self::iter::{ Drain, IntoIter, IntoKeys, IntoValues, Iter, IterMut, Keys, Splice, Values, ValuesMut, }; +pub use self::mutable::MutableKeys; pub use self::slice::Slice; -pub use crate::mutable_keys::MutableKeys; #[cfg(feature = "rayon")] pub use crate::rayon::map as rayon; @@ -533,9 +534,9 @@ where /// Return `true` if an equivalent to `key` exists in the map. /// /// Computes in **O(1)** time (average). - pub fn contains_key(&self, key: &Q) -> bool + pub fn contains_key(&self, key: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.get_index_of(key).is_some() } @@ -544,9 +545,9 @@ where /// else `None`. /// /// Computes in **O(1)** time (average). - pub fn get(&self, key: &Q) -> Option<&V> + pub fn get(&self, key: &Q) -> Option<&V> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &self.as_entries()[i]; @@ -560,9 +561,9 @@ where /// if it is present, else `None`. /// /// Computes in **O(1)** time (average). - pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> + pub fn get_key_value(&self, key: &Q) -> Option<(&K, &V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &self.as_entries()[i]; @@ -573,9 +574,9 @@ where } /// Return item index, key and value - pub fn get_full(&self, key: &Q) -> Option<(usize, &K, &V)> + pub fn get_full(&self, key: &Q) -> Option<(usize, &K, &V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &self.as_entries()[i]; @@ -588,9 +589,9 @@ where /// Return item index, if it exists in the map /// /// Computes in **O(1)** time (average). - pub fn get_index_of(&self, key: &Q) -> Option + pub fn get_index_of(&self, key: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { match self.as_entries() { [] => None, @@ -602,9 +603,9 @@ where } } - pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &mut self.as_entries_mut()[i]; @@ -614,9 +615,9 @@ where } } - pub fn get_full_mut(&mut self, key: &Q) -> Option<(usize, &K, &mut V)> + pub fn get_full_mut(&mut self, key: &Q) -> Option<(usize, &K, &mut V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &mut self.as_entries_mut()[i]; @@ -635,9 +636,9 @@ where /// [`.shift_remove(key)`][Self::shift_remove] instead. #[deprecated(note = "`remove` disrupts the map order -- \ use `swap_remove` or `shift_remove` for explicit behavior.")] - pub fn remove(&mut self, key: &Q) -> Option + pub fn remove(&mut self, key: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_remove(key) } @@ -650,9 +651,9 @@ where /// use [`.shift_remove_entry(key)`][Self::shift_remove_entry] instead. #[deprecated(note = "`remove_entry` disrupts the map order -- \ use `swap_remove_entry` or `shift_remove_entry` for explicit behavior.")] - pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> + pub fn remove_entry(&mut self, key: &Q) -> Option<(K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_remove_entry(key) } @@ -667,9 +668,9 @@ where /// Return `None` if `key` is not in map. /// /// Computes in **O(1)** time (average). - pub fn swap_remove(&mut self, key: &Q) -> Option + pub fn swap_remove(&mut self, key: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_remove_full(key).map(third) } @@ -683,9 +684,9 @@ where /// Return `None` if `key` is not in map. /// /// Computes in **O(1)** time (average). - pub fn swap_remove_entry(&mut self, key: &Q) -> Option<(K, V)> + pub fn swap_remove_entry(&mut self, key: &Q) -> Option<(K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { match self.swap_remove_full(key) { Some((_, key, value)) => Some((key, value)), @@ -703,9 +704,9 @@ where /// Return `None` if `key` is not in map. /// /// Computes in **O(1)** time (average). - pub fn swap_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> + pub fn swap_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { match self.as_entries() { [x] if key.equivalent(&x.key) => { @@ -730,9 +731,9 @@ where /// Return `None` if `key` is not in map. /// /// Computes in **O(n)** time (average). - pub fn shift_remove(&mut self, key: &Q) -> Option + pub fn shift_remove(&mut self, key: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.shift_remove_full(key).map(third) } @@ -746,9 +747,9 @@ where /// Return `None` if `key` is not in map. /// /// Computes in **O(n)** time (average). - pub fn shift_remove_entry(&mut self, key: &Q) -> Option<(K, V)> + pub fn shift_remove_entry(&mut self, key: &Q) -> Option<(K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { match self.shift_remove_full(key) { Some((_, key, value)) => Some((key, value)), @@ -766,9 +767,9 @@ where /// Return `None` if `key` is not in map. /// /// Computes in **O(n)** time (average). - pub fn shift_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> + pub fn shift_remove_full(&mut self, key: &Q) -> Option<(usize, K, V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { match self.as_entries() { [x] if key.equivalent(&x.key) => { @@ -808,13 +809,6 @@ impl IndexMap { self.core.retain_in_order(move |k, v| keep(k, v)); } - pub(crate) fn retain_mut(&mut self, keep: F) - where - F: FnMut(&mut K, &mut V) -> bool, - { - self.core.retain_in_order(keep); - } - /// Sort the map’s key-value pairs by the default ordering of the keys. /// /// This is a stable sort -- but equivalent keys should not normally coexist in diff --git a/src/map/core/raw_entry_v1.rs b/src/map/core/raw_entry_v1.rs index a3848149..87e532d5 100644 --- a/src/map/core/raw_entry_v1.rs +++ b/src/map/core/raw_entry_v1.rs @@ -198,18 +198,18 @@ impl fmt::Debug for RawEntryBuilder<'_, K, V, S> { impl<'a, K, V, S> RawEntryBuilder<'a, K, V, S> { /// Access an entry by key. - pub fn from_key(self, key: &Q) -> Option<(&'a K, &'a V)> + pub fn from_key(self, key: &Q) -> Option<(&'a K, &'a V)> where S: BuildHasher, - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.get_key_value(key) } /// Access an entry by a key and its hash. - pub fn from_key_hashed_nocheck(self, hash: u64, key: &Q) -> Option<(&'a K, &'a V)> + pub fn from_key_hashed_nocheck(self, hash: u64, key: &Q) -> Option<(&'a K, &'a V)> where - Q: Equivalent, + Q: ?Sized + Equivalent, { let hash = HashValue(hash as usize); let i = self.map.core.get_index_of(hash, key)?; @@ -265,19 +265,19 @@ impl fmt::Debug for RawEntryBuilderMut<'_, K, V, S> { impl<'a, K, V, S> RawEntryBuilderMut<'a, K, V, S> { /// Access an entry by key. - pub fn from_key(self, key: &Q) -> RawEntryMut<'a, K, V, S> + pub fn from_key(self, key: &Q) -> RawEntryMut<'a, K, V, S> where S: BuildHasher, - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { let hash = self.map.hash(key); self.from_key_hashed_nocheck(hash.get(), key) } /// Access an entry by a key and its hash. - pub fn from_key_hashed_nocheck(self, hash: u64, key: &Q) -> RawEntryMut<'a, K, V, S> + pub fn from_key_hashed_nocheck(self, hash: u64, key: &Q) -> RawEntryMut<'a, K, V, S> where - Q: Equivalent, + Q: ?Sized + Equivalent, { self.from_hash(hash, |k| Q::equivalent(key, k)) } diff --git a/src/mutable_keys.rs b/src/map/mutable.rs similarity index 83% rename from src/mutable_keys.rs rename to src/map/mutable.rs index 917af8c6..7df32594 100644 --- a/src/mutable_keys.rs +++ b/src/map/mutable.rs @@ -6,7 +6,7 @@ use super::{Bucket, Entries, Equivalent, IndexMap}; /// /// 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 hashmap **if the modification +/// 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. @@ -23,12 +23,9 @@ pub trait MutableKeys: private::Sealed { /// Return item index, mutable reference to key and value /// /// Computes in **O(1)** time (average). - fn get_full_mut2( - &mut self, - key: &Q, - ) -> Option<(usize, &mut Self::Key, &mut Self::Value)> + fn get_full_mut2(&mut self, key: &Q) -> Option<(usize, &mut Self::Key, &mut Self::Value)> where - Q: Hash + Equivalent; + Q: ?Sized + Hash + Equivalent; /// Return mutable reference to key and value at an index. /// @@ -49,7 +46,7 @@ pub trait MutableKeys: private::Sealed { F: FnMut(&mut Self::Key, &mut Self::Value) -> bool; } -/// Opt-in mutable access to keys. +/// Opt-in mutable access to [`IndexMap`] keys. /// /// See [`MutableKeys`] for more information. impl MutableKeys for IndexMap @@ -59,9 +56,9 @@ where type Key = K; type Value = V; - fn get_full_mut2(&mut self, key: &Q) -> Option<(usize, &mut K, &mut V)> + fn get_full_mut2(&mut self, key: &Q) -> Option<(usize, &mut K, &mut V)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { if let Some(i) = self.get_index_of(key) { let entry = &mut self.as_entries_mut()[i]; @@ -79,7 +76,7 @@ where where F: FnMut(&mut K, &mut V) -> bool, { - self.retain_mut(keep) + self.core.retain_in_order(keep); } } diff --git a/src/map/slice.rs b/src/map/slice.rs index ce001ee8..b2f00f48 100644 --- a/src/map/slice.rs +++ b/src/map/slice.rs @@ -426,7 +426,6 @@ impl_index!( #[cfg(test)] mod tests { use super::*; - use alloc::vec::Vec; #[test] fn slice_index() { diff --git a/src/set.rs b/src/set.rs index 5774349d..b5bd05f1 100644 --- a/src/set.rs +++ b/src/set.rs @@ -1,6 +1,7 @@ //! A hash set implemented using [`IndexMap`] mod iter; +mod mutable; mod slice; #[cfg(test)] @@ -9,6 +10,7 @@ mod tests; pub use self::iter::{ Difference, Drain, Intersection, IntoIter, Iter, Splice, SymmetricDifference, Union, }; +pub use self::mutable::MutableValues; pub use self::slice::Slice; #[cfg(feature = "rayon")] @@ -508,9 +510,9 @@ where /// Return `true` if an equivalent to `value` exists in the set. /// /// Computes in **O(1)** time (average). - pub fn contains(&self, value: &Q) -> bool + pub fn contains(&self, value: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.contains_key(value) } @@ -519,17 +521,17 @@ where /// else `None`. /// /// Computes in **O(1)** time (average). - pub fn get(&self, value: &Q) -> Option<&T> + pub fn get(&self, value: &Q) -> Option<&T> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.get_key_value(value).map(|(x, &())| x) } /// Return item index and value - pub fn get_full(&self, value: &Q) -> Option<(usize, &T)> + pub fn get_full(&self, value: &Q) -> Option<(usize, &T)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.get_full(value).map(|(i, x, &())| (i, x)) } @@ -537,9 +539,9 @@ where /// Return item index, if it exists in the set /// /// Computes in **O(1)** time (average). - pub fn get_index_of(&self, value: &Q) -> Option + pub fn get_index_of(&self, value: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.get_index_of(value) } @@ -552,9 +554,9 @@ where /// [`.shift_remove(value)`][Self::shift_remove] instead. #[deprecated(note = "`remove` disrupts the set order -- \ use `swap_remove` or `shift_remove` for explicit behavior.")] - pub fn remove(&mut self, value: &Q) -> bool + pub fn remove(&mut self, value: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_remove(value) } @@ -568,9 +570,9 @@ where /// Return `false` if `value` was not in the set. /// /// Computes in **O(1)** time (average). - pub fn swap_remove(&mut self, value: &Q) -> bool + pub fn swap_remove(&mut self, value: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.swap_remove(value).is_some() } @@ -584,9 +586,9 @@ where /// Return `false` if `value` was not in the set. /// /// Computes in **O(n)** time (average). - pub fn shift_remove(&mut self, value: &Q) -> bool + pub fn shift_remove(&mut self, value: &Q) -> bool where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.shift_remove(value).is_some() } @@ -600,9 +602,9 @@ where /// [`.shift_take(value)`][Self::shift_take] instead. #[deprecated(note = "`take` disrupts the set order -- \ use `swap_take` or `shift_take` for explicit behavior.")] - pub fn take(&mut self, value: &Q) -> Option + pub fn take(&mut self, value: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.swap_take(value) } @@ -617,9 +619,9 @@ where /// Return `None` if `value` was not in the set. /// /// Computes in **O(1)** time (average). - pub fn swap_take(&mut self, value: &Q) -> Option + pub fn swap_take(&mut self, value: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.swap_remove_entry(value).map(|(x, ())| x) } @@ -634,9 +636,9 @@ where /// Return `None` if `value` was not in the set. /// /// Computes in **O(n)** time (average). - pub fn shift_take(&mut self, value: &Q) -> Option + pub fn shift_take(&mut self, value: &Q) -> Option where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.shift_remove_entry(value).map(|(x, ())| x) } @@ -648,9 +650,9 @@ where /// the position of what used to be the last element!** /// /// Return `None` if `value` was not in the set. - pub fn swap_remove_full(&mut self, value: &Q) -> Option<(usize, T)> + pub fn swap_remove_full(&mut self, value: &Q) -> Option<(usize, T)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.swap_remove_full(value).map(|(i, x, ())| (i, x)) } @@ -662,9 +664,9 @@ where /// **This perturbs the index of all of those elements!** /// /// Return `None` if `value` was not in the set. - pub fn shift_remove_full(&mut self, value: &Q) -> Option<(usize, T)> + pub fn shift_remove_full(&mut self, value: &Q) -> Option<(usize, T)> where - Q: Hash + Equivalent, + Q: ?Sized + Hash + Equivalent, { self.map.shift_remove_full(value).map(|(i, x, ())| (i, x)) } diff --git a/src/set/mutable.rs b/src/set/mutable.rs new file mode 100644 index 00000000..20eaa112 --- /dev/null +++ b/src/set/mutable.rs @@ -0,0 +1,86 @@ +use core::hash::{BuildHasher, Hash}; + +use super::{Equivalent, IndexSet}; +use crate::map::MutableKeys; + +/// Opt-in mutable access to [`IndexSet`] values. +/// +/// These methods expose `&mut T`, mutable references to the value as it is stored +/// in the set. +/// You are allowed to modify the values in the set **if the modification +/// does not change the value’s hash and equality**. +/// +/// If values 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 `IndexSet`. +/// +/// This trait is sealed and cannot be implemented for types outside this crate. +pub trait MutableValues: private::Sealed { + type Value; + + /// Return item index and mutable reference to the value + /// + /// Computes in **O(1)** time (average). + fn get_full_mut2(&mut self, value: &Q) -> Option<(usize, &mut Self::Value)> + where + Q: ?Sized + Hash + Equivalent; + + /// Return mutable reference to the value at an index. + /// + /// Valid indices are *0 <= index < self.len()* + /// + /// Computes in **O(1)** time. + fn get_index_mut2(&mut self, index: usize) -> Option<&mut Self::Value>; + + /// Scan through each value in the set and keep those where the + /// closure `keep` returns `true`. + /// + /// The values are visited in order, and remaining values keep their order. + /// + /// Computes in **O(n)** time (average). + fn retain2(&mut self, keep: F) + where + F: FnMut(&mut Self::Value) -> bool; +} + +/// Opt-in mutable access to [`IndexSet`] values. +/// +/// See [`MutableValues`] for more information. +impl MutableValues for IndexSet +where + S: BuildHasher, +{ + type Value = T; + + fn get_full_mut2(&mut self, value: &Q) -> Option<(usize, &mut T)> + where + Q: ?Sized + Hash + Equivalent, + { + match self.map.get_full_mut2(value) { + Some((index, value, ())) => Some((index, value)), + None => None, + } + } + + fn get_index_mut2(&mut self, index: usize) -> Option<&mut T> { + match self.map.get_index_mut2(index) { + Some((value, ())) => Some(value), + None => None, + } + } + + fn retain2(&mut self, mut keep: F) + where + F: FnMut(&mut T) -> bool, + { + self.map.retain2(move |value, ()| keep(value)); + } +} + +mod private { + pub trait Sealed {} + + impl Sealed for super::IndexSet {} +} diff --git a/src/set/slice.rs b/src/set/slice.rs index be006c97..9fc208c7 100644 --- a/src/set/slice.rs +++ b/src/set/slice.rs @@ -293,7 +293,6 @@ impl_index!( #[cfg(test)] mod tests { use super::*; - use alloc::vec::Vec; #[test] fn slice_index() {