X-Git-Url: https://git.chrismorgan.info/anymap/blobdiff_plain/27eca551823bac40421ae7ee6e83e7541790b723..e04b8b4d6ecf155f4ac9fedde3effe55e5f74ae0:/src/lib.rs diff --git a/src/lib.rs b/src/lib.rs index beef1bd..560a73d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -20,7 +20,7 @@ extern crate alloc; #[cfg(not(feature = "std"))] use alloc::boxed::Box; -use any::{UncheckedAnyExt, IntoBox}; +use any::{Downcast, IntoBox}; pub use any::CloneAny; #[cfg(all(feature = "std", not(feature = "hashbrown")))] @@ -48,87 +48,6 @@ pub use hashbrown::hash_map as raw_hash_map; use self::raw_hash_map::HashMap; -macro_rules! impl_common_methods { - ( - field: $t:ident.$field:ident; - new() => $new:expr; - with_capacity($with_capacity_arg:ident) => $with_capacity:expr; - ) => { - impl $t { - /// Create an empty collection. - #[inline] - pub fn new() -> $t { - $t { - $field: $new, - } - } - - /// Creates an empty collection with the given initial capacity. - #[inline] - pub fn with_capacity($with_capacity_arg: usize) -> $t { - $t { - $field: $with_capacity, - } - } - - /// Returns the number of elements the collection can hold without reallocating. - #[inline] - pub fn capacity(&self) -> usize { - self.$field.capacity() - } - - /// Reserves capacity for at least `additional` more elements to be inserted - /// in the collection. The collection may reserve more space to avoid - /// frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new allocation size overflows `usize`. - #[inline] - pub fn reserve(&mut self, additional: usize) { - self.$field.reserve(additional) - } - - /// Shrinks the capacity of the collection as much as possible. It will drop - /// down as much as possible while maintaining the internal rules - /// and possibly leaving some space in accordance with the resize policy. - #[inline] - pub fn shrink_to_fit(&mut self) { - self.$field.shrink_to_fit() - } - - // Additional stable methods (as of 1.60.0-nightly) that could be added: - // try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> (1.57.0) - // shrink_to(&mut self, min_capacity: usize) (1.56.0) - - /// Returns the number of items in the collection. - #[inline] - pub fn len(&self) -> usize { - self.$field.len() - } - - /// Returns true if there are no items in the collection. - #[inline] - pub fn is_empty(&self) -> bool { - self.$field.is_empty() - } - - /// Removes all items from the collection. Keeps the allocated memory for reuse. - #[inline] - pub fn clear(&mut self) { - self.$field.clear() - } - } - - impl Default for $t { - #[inline] - fn default() -> $t { - $t::new() - } - } - } -} - mod any; /// Raw access to the underlying `HashMap`. @@ -189,12 +108,12 @@ pub type RawMap = HashMap, BuildHasherDefault>; /// /// Values containing non-static references are not permitted. #[derive(Debug)] -pub struct Map { +pub struct Map { raw: RawMap, } // #[derive(Clone)] would want A to implement Clone, but in reality it’s only Box that can. -impl Clone for Map where Box: Clone { +impl Clone for Map where Box: Clone { #[inline] fn clone(&self) -> Map { Map { @@ -210,13 +129,78 @@ impl Clone for Map where Box: Clone { /// It’s a bit sad, really. Ah well, I guess this approach will do. pub type AnyMap = Map; -impl_common_methods! { - field: Map.raw; - new() => RawMap::with_hasher(Default::default()); - with_capacity(capacity) => RawMap::with_capacity_and_hasher(capacity, Default::default()); +impl Default for Map { + #[inline] + fn default() -> Map { + Map::new() + } } -impl Map { +impl Map { + /// Create an empty collection. + #[inline] + pub fn new() -> Map { + Map { + raw: RawMap::with_hasher(Default::default()), + } + } + + /// Creates an empty collection with the given initial capacity. + #[inline] + pub fn with_capacity(capacity: usize) -> Map { + Map { + raw: RawMap::with_capacity_and_hasher(capacity, Default::default()), + } + } + + /// Returns the number of elements the collection can hold without reallocating. + #[inline] + pub fn capacity(&self) -> usize { + self.raw.capacity() + } + + /// Reserves capacity for at least `additional` more elements to be inserted + /// in the collection. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new allocation size overflows `usize`. + #[inline] + pub fn reserve(&mut self, additional: usize) { + self.raw.reserve(additional) + } + + /// Shrinks the capacity of the collection as much as possible. It will drop + /// down as much as possible while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + #[inline] + pub fn shrink_to_fit(&mut self) { + self.raw.shrink_to_fit() + } + + // Additional stable methods (as of 1.60.0-nightly) that could be added: + // try_reserve(&mut self, additional: usize) -> Result<(), TryReserveError> (1.57.0) + // shrink_to(&mut self, min_capacity: usize) (1.56.0) + + /// Returns the number of items in the collection. + #[inline] + pub fn len(&self) -> usize { + self.raw.len() + } + + /// Returns true if there are no items in the collection. + #[inline] + pub fn is_empty(&self) -> bool { + self.raw.is_empty() + } + + /// Removes all items from the collection. Keeps the allocated memory for reuse. + #[inline] + pub fn clear(&mut self) { + self.raw.clear() + } + /// Returns a reference to the value stored in the collection for the type `T`, if it exists. #[inline] pub fn get>(&self) -> Option<&T> { @@ -237,10 +221,8 @@ impl Map { /// Otherwise, `None` is returned. #[inline] pub fn insert>(&mut self, value: T) -> Option { - unsafe { - self.raw.insert(TypeId::of::(), value.into_box()) - .map(|any| *any.downcast_unchecked::()) - } + self.raw.insert(TypeId::of::(), value.into_box()) + .map(|any| unsafe { *any.downcast_unchecked::() }) } // rustc 1.60.0-nightly has another method try_insert that would be nice to add when stable. @@ -354,17 +336,17 @@ impl Map { } } -impl Extend> for Map { +impl Extend> for Map { #[inline] fn extend>>(&mut self, iter: T) { for item in iter { - let _ = self.raw.insert(item.type_id(), item); + let _ = self.raw.insert(Downcast::type_id(&*item), item); } } } /// A view into a single occupied location in an `Map`. -pub struct OccupiedEntry<'a, A: ?Sized + UncheckedAnyExt, V: 'a> { +pub struct OccupiedEntry<'a, A: ?Sized + Downcast, V: 'a> { #[cfg(all(feature = "std", not(feature = "hashbrown")))] inner: raw_hash_map::OccupiedEntry<'a, TypeId, Box>, #[cfg(feature = "hashbrown")] @@ -373,7 +355,7 @@ pub struct OccupiedEntry<'a, A: ?Sized + UncheckedAnyExt, V: 'a> { } /// A view into a single empty location in an `Map`. -pub struct VacantEntry<'a, A: ?Sized + UncheckedAnyExt, V: 'a> { +pub struct VacantEntry<'a, A: ?Sized + Downcast, V: 'a> { #[cfg(all(feature = "std", not(feature = "hashbrown")))] inner: raw_hash_map::VacantEntry<'a, TypeId, Box>, #[cfg(feature = "hashbrown")] @@ -382,14 +364,14 @@ pub struct VacantEntry<'a, A: ?Sized + UncheckedAnyExt, V: 'a> { } /// A view into a single location in an `Map`, which may be vacant or occupied. -pub enum Entry<'a, A: ?Sized + UncheckedAnyExt, V: 'a> { +pub enum Entry<'a, A: ?Sized + Downcast, V: 'a> { /// An occupied Entry Occupied(OccupiedEntry<'a, A, V>), /// A vacant Entry Vacant(VacantEntry<'a, A, V>), } -impl<'a, A: ?Sized + UncheckedAnyExt, V: IntoBox> Entry<'a, A, V> { +impl<'a, A: ?Sized + Downcast, V: IntoBox> Entry<'a, A, V> { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. #[inline] @@ -409,9 +391,35 @@ impl<'a, A: ?Sized + UncheckedAnyExt, V: IntoBox> Entry<'a, A, V> { Entry::Vacant(inner) => inner.insert(default()), } } + + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. + #[inline] + pub fn or_default(self) -> &'a mut V where V: Default { + match self { + Entry::Occupied(inner) => inner.into_mut(), + Entry::Vacant(inner) => inner.insert(Default::default()), + } + } + + /// Provides in-place mutable access to an occupied entry before any potential inserts into the + /// map. + #[inline] + pub fn and_modify(self, f: F) -> Self { + match self { + Entry::Occupied(mut inner) => { + f(inner.get_mut()); + Entry::Occupied(inner) + }, + Entry::Vacant(inner) => Entry::Vacant(inner), + } + } + + // Additional stable methods (as of 1.60.0-nightly) that could be added: + // insert_entry(self, value: V) -> OccupiedEntry<'a, K, V> (1.59.0) } -impl<'a, A: ?Sized + UncheckedAnyExt, V: IntoBox> OccupiedEntry<'a, A, V> { +impl<'a, A: ?Sized + Downcast, V: IntoBox> OccupiedEntry<'a, A, V> { /// Gets a reference to the value in the entry #[inline] pub fn get(&self) -> &V { @@ -444,7 +452,7 @@ impl<'a, A: ?Sized + UncheckedAnyExt, V: IntoBox> OccupiedEntry<'a, A, V> { } } -impl<'a, A: ?Sized + UncheckedAnyExt, V: IntoBox> VacantEntry<'a, A, V> { +impl<'a, A: ?Sized + Downcast, V: IntoBox> VacantEntry<'a, A, V> { /// Sets the value of the entry with the VacantEntry's key, /// and returns a mutable reference to it #[inline] @@ -635,4 +643,13 @@ mod tests { verify_hashing_with(TypeId::of::<&str>()); verify_hashing_with(TypeId::of::>()); } + + #[test] + fn test_extend() { + let mut map = AnyMap::new(); + map.extend([Box::new(123) as Box, Box::new(456), Box::new(true)]); + assert_eq!(map.get(), Some(&456)); + assert_eq!(map.get::(), Some(&true)); + assert!(map.get::>().is_none()); + } }