#[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")))]
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<A: ?Sized + UncheckedAnyExt> $t<A> {
- /// Create an empty collection.
- #[inline]
- pub fn new() -> $t<A> {
- $t {
- $field: $new,
- }
- }
-
- /// Creates an empty collection with the given initial capacity.
- #[inline]
- pub fn with_capacity($with_capacity_arg: usize) -> $t<A> {
- $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<A: ?Sized + UncheckedAnyExt> Default for $t<A> {
- #[inline]
- fn default() -> $t<A> {
- $t::new()
- }
- }
- }
-}
-
mod any;
/// Raw access to the underlying `HashMap`.
///
/// Values containing non-static references are not permitted.
#[derive(Debug)]
-pub struct Map<A: ?Sized + UncheckedAnyExt = dyn Any> {
+pub struct Map<A: ?Sized + Downcast = dyn Any> {
raw: RawMap<A>,
}
// #[derive(Clone)] would want A to implement Clone, but in reality it’s only Box<A> that can.
-impl<A: ?Sized + UncheckedAnyExt> Clone for Map<A> where Box<A>: Clone {
+impl<A: ?Sized + Downcast> Clone for Map<A> where Box<A>: Clone {
#[inline]
fn clone(&self) -> Map<A> {
Map {
/// It’s a bit sad, really. Ah well, I guess this approach will do.
pub type AnyMap = Map<dyn Any>;
-impl_common_methods! {
- field: Map.raw;
- new() => RawMap::with_hasher(Default::default());
- with_capacity(capacity) => RawMap::with_capacity_and_hasher(capacity, Default::default());
+impl<A: ?Sized + Downcast> Default for Map<A> {
+ #[inline]
+ fn default() -> Map<A> {
+ Map::new()
+ }
}
-impl<A: ?Sized + UncheckedAnyExt> Map<A> {
+impl<A: ?Sized + Downcast> Map<A> {
+ /// Create an empty collection.
+ #[inline]
+ pub fn new() -> Map<A> {
+ Map {
+ raw: RawMap::with_hasher(Default::default()),
+ }
+ }
+
+ /// Creates an empty collection with the given initial capacity.
+ #[inline]
+ pub fn with_capacity(capacity: usize) -> Map<A> {
+ 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<T: IntoBox<A>>(&self) -> Option<&T> {
/// Otherwise, `None` is returned.
#[inline]
pub fn insert<T: IntoBox<A>>(&mut self, value: T) -> Option<T> {
- unsafe {
- self.raw.insert(TypeId::of::<T>(), value.into_box())
- .map(|any| *any.downcast_unchecked::<T>())
- }
+ self.raw.insert(TypeId::of::<T>(), value.into_box())
+ .map(|any| unsafe { *any.downcast_unchecked::<T>() })
}
// rustc 1.60.0-nightly has another method try_insert that would be nice to add when stable.
}
}
-impl<A: ?Sized + UncheckedAnyExt> Extend<Box<A>> for Map<A> {
+impl<A: ?Sized + Downcast> Extend<Box<A>> for Map<A> {
#[inline]
fn extend<T: IntoIterator<Item = Box<A>>>(&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<A>>,
#[cfg(feature = "hashbrown")]
}
/// 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<A>>,
#[cfg(feature = "hashbrown")]
}
/// 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<A>> Entry<'a, A, V> {
+impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> 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]
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<F: FnOnce(&mut V)>(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<A>> OccupiedEntry<'a, A, V> {
+impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> OccupiedEntry<'a, A, V> {
/// Gets a reference to the value in the entry
#[inline]
pub fn get(&self) -> &V {
}
}
-impl<'a, A: ?Sized + UncheckedAnyExt, V: IntoBox<A>> VacantEntry<'a, A, V> {
+impl<'a, A: ?Sized + Downcast, V: IntoBox<A>> VacantEntry<'a, A, V> {
/// Sets the value of the entry with the VacantEntry's key,
/// and returns a mutable reference to it
#[inline]
verify_hashing_with(TypeId::of::<&str>());
verify_hashing_with(TypeId::of::<Vec<u8>>());
}
+
+ #[test]
+ fn test_extend() {
+ let mut map = AnyMap::new();
+ map.extend([Box::new(123) as Box<dyn Any>, Box::new(456), Box::new(true)]);
+ assert_eq!(map.get(), Some(&456));
+ assert_eq!(map.get::<bool>(), Some(&true));
+ assert!(map.get::<Box<dyn Any>>().is_none());
+ }
}