From 210b027a67e42cd196f07cf8b2f0ee498a4b1a80 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Wed, 20 Mar 2024 16:00:06 -0700 Subject: [PATCH] Opt-in mutable access on IndexSet --- src/lib.rs | 3 +- src/map.rs | 10 +-- src/{mutable_keys.rs => map/mutable.rs} | 6 +- src/set.rs | 2 + src/set/mutable.rs | 86 +++++++++++++++++++++++++ 5 files changed, 94 insertions(+), 13 deletions(-) rename src/{mutable_keys.rs => map/mutable.rs} (94%) create mode 100644 src/set/mutable.rs 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..f6d548bf 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; @@ -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/mutable_keys.rs b/src/map/mutable.rs similarity index 94% rename from src/mutable_keys.rs rename to src/map/mutable.rs index 917af8c6..f45dd5e2 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. @@ -49,7 +49,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 @@ -79,7 +79,7 @@ where where F: FnMut(&mut K, &mut V) -> bool, { - self.retain_mut(keep) + self.core.retain_in_order(keep); } } diff --git a/src/set.rs b/src/set.rs index 5774349d..671b2fa9 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")] diff --git a/src/set/mutable.rs b/src/set/mutable.rs new file mode 100644 index 00000000..3acecf3f --- /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: 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: 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 {} +}