use std::any::{Any, TypeId}; use rustc_hash::FxHashMap; use std::collections::hash_map; use std::marker::PhantomData; /// A view into an occupied entry in a `TypeMap`. #[derive(Debug)] pub struct OccupiedEntry<'a, T> { data: hash_map::OccupiedEntry<'a, TypeId, Box>, marker: PhantomData, } impl<'a, T: 'static> OccupiedEntry<'a, T> { /// Gets a reference to the value in the entry. pub fn get(&self) -> &T { self.data.get().downcast_ref().unwrap() } ///Gets a mutable reference to the value in the entry. pub fn get_mut(&mut self) -> &mut T { self.data.get_mut().downcast_mut().unwrap() } /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. pub fn into_mut(self) -> &'a mut T { self.data.into_mut().downcast_mut().unwrap() } /// Sets the value of the entry, and returns the entry's old value. pub fn insert(&mut self, value: T) -> T { self.data.insert(Box::new(value)).downcast().map(|boxed| *boxed).unwrap() } /// Takes the value out of the entry, and returns it. pub fn remove(self) -> T { self.data.remove().downcast().map(|boxed| *boxed).unwrap() } } /// A view into a vacant entry in a `TypeMap`. #[derive(Debug)] pub struct VacantEntry<'a, T> { data: hash_map::VacantEntry<'a, TypeId, Box>, marker: PhantomData, } impl<'a, T: 'static> VacantEntry<'a, T> { /// Sets the value of the entry with the key of the `VacantEntry`, and returns a mutable reference to it. pub fn insert(self, value: T) -> &'a mut T { self.data.insert(Box::new(value)).downcast_mut().unwrap() } } /// A view into a single entry in a map, which may either be vacant or occupied. #[derive(Debug)] pub enum Entry<'a, T> { Occupied(OccupiedEntry<'a, T>), Vacant(VacantEntry<'a, T>), } impl<'a, T: 'static> Entry<'a, T> { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. pub fn or_insert(self, default: T) -> &'a mut T { match self { Entry::Occupied(inner) => inner.into_mut(), Entry::Vacant(inner) => inner.insert(default), } } /// Ensures a value is in the entry by inserting the result of the default function if empty, and returns /// a mutable reference to the value in the entry. pub fn or_insert_with T>(self, default: F) -> &'a mut T { match self { Entry::Occupied(inner) => inner.into_mut(), Entry::Vacant(inner) => inner.insert(default()), } } } #[derive(Debug, Default)] /// The typemap container pub struct TypeMap { map: Option>>, } impl TypeMap { /// Create an empty `TypeMap`. #[inline] pub fn new() -> Self { Self { map: None } } /// Insert a value into this `TypeMap`. /// /// If a value of this type already exists, it will be returned. pub fn insert(&mut self, val: T) -> Option { self.map .get_or_insert_with(|| FxHashMap::default()) .insert(TypeId::of::(), Box::new(val)) .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed)) } /// Check if container contains value for type pub fn contains(&self) -> bool { self.map.as_ref().and_then(|m| m.get(&TypeId::of::())).is_some() } /// Get a reference to a value previously inserted on this `TypeMap`. pub fn get(&self) -> Option<&T> { self.map .as_ref() .and_then(|m| m.get(&TypeId::of::())) .and_then(|boxed| boxed.downcast_ref()) } /// Get a mutable reference to a value previously inserted on this `TypeMap`. pub fn get_mut(&mut self) -> Option<&mut T> { self.map .as_mut() .and_then(|m| m.get_mut(&TypeId::of::())) .and_then(|boxed| boxed.downcast_mut()) } /// Remove a value from this `TypeMap`. /// /// If a value of this type exists, it will be returned. pub fn remove(&mut self) -> Option { self.map .as_mut() .and_then(|m| m.remove(&TypeId::of::())) .and_then(|boxed| boxed.downcast().ok().map(|boxed| *boxed)) } /// Clear the `TypeMap` of all inserted values. #[inline] pub fn clear(&mut self) { self.map = None; } /// Get an entry in the `TypeMap` for in-place manipulation. pub fn entry(&mut self) -> Entry { match self.map.get_or_insert_with(|| FxHashMap::default()).entry(TypeId::of::()) { hash_map::Entry::Occupied(e) => { Entry::Occupied(OccupiedEntry { data: e, marker: PhantomData }) } hash_map::Entry::Vacant(e) => { Entry::Vacant(VacantEntry { data: e, marker: PhantomData }) } } } } /// Provides the same `TypeMap` container, but with `Send` + `Sync` bounds on values pub mod concurrent { use std::any::{Any, TypeId}; use rustc_hash::FxHashMap; use std::collections::hash_map; use std::marker::PhantomData; /// A view into an occupied entry in a `TypeMap`. #[derive(Debug)] pub struct OccupiedEntry<'a, T> { data: hash_map::OccupiedEntry<'a, TypeId, Box>, marker: PhantomData, } impl<'a, T: 'static + Send + Sync> OccupiedEntry<'a, T> { /// Gets a reference to the value in the entry. pub fn get(&self) -> &T { self.data.get().downcast_ref().unwrap() } ///Gets a mutable reference to the value in the entry. pub fn get_mut(&mut self) -> &mut T { self.data.get_mut().downcast_mut().unwrap() } /// Converts the `OccupiedEntry` into a mutable reference to the value in the entry /// with a lifetime bound to the map itself. pub fn into_mut(self) -> &'a mut T { self.data.into_mut().downcast_mut().unwrap() } /// Sets the value of the entry, and returns the entry's old value. pub fn insert(&mut self, value: T) -> T { (self.data.insert(Box::new(value)) as Box) .downcast() .map(|boxed| *boxed) .unwrap() } /// Takes the value out of the entry, and returns it. pub fn remove(self) -> T { (self.data.remove() as Box).downcast().map(|boxed| *boxed).unwrap() } } /// A view into a vacant entry in a `TypeMap`. #[derive(Debug)] pub struct VacantEntry<'a, T> { data: hash_map::VacantEntry<'a, TypeId, Box>, marker: PhantomData, } impl<'a, T: 'static + Send + Sync> VacantEntry<'a, T> { /// Sets the value of the entry with the key of the `VacantEntry`, and returns a mutable reference to it. pub fn insert(self, value: T) -> &'a mut T { self.data.insert(Box::new(value)).downcast_mut().unwrap() } } /// A view into a single entry in a map, which may either be vacant or occupied. #[derive(Debug)] pub enum Entry<'a, T> { Occupied(OccupiedEntry<'a, T>), Vacant(VacantEntry<'a, T>), } impl<'a, T: 'static + Send + Sync> Entry<'a, T> { /// Ensures a value is in the entry by inserting the default if empty, and returns /// a mutable reference to the value in the entry. pub fn or_insert(self, default: T) -> &'a mut T { match self { Entry::Occupied(inner) => inner.into_mut(), Entry::Vacant(inner) => inner.insert(default), } } /// Ensures a value is in the entry by inserting the result of the default function if empty, and returns /// a mutable reference to the value in the entry. pub fn or_insert_with T>(self, default: F) -> &'a mut T { match self { Entry::Occupied(inner) => inner.into_mut(), Entry::Vacant(inner) => inner.insert(default()), } } } #[derive(Debug, Default)] /// The typemap container pub struct TypeMap { map: Option>>, } impl TypeMap { /// Create an empty `TypeMap`. #[inline] pub fn new() -> Self { Self { map: None } } /// Insert a value into this `TypeMap`. /// /// If a value of this type already exists, it will be returned. pub fn insert(&mut self, val: T) -> Option { self.map .get_or_insert_with(|| FxHashMap::default()) .insert(TypeId::of::(), Box::new(val)) .and_then(|boxed| (boxed as Box).downcast().ok().map(|boxed| *boxed)) } /// Check if container contains value for type pub fn contains(&self) -> bool { self.map.as_ref().and_then(|m| m.get(&TypeId::of::())).is_some() } /// Get a reference to a value previously inserted on this `TypeMap`. pub fn get(&self) -> Option<&T> { self.map .as_ref() .and_then(|m| m.get(&TypeId::of::())) .and_then(|boxed| boxed.downcast_ref()) } /// Get a mutable reference to a value previously inserted on this `TypeMap`. pub fn get_mut(&mut self) -> Option<&mut T> { self.map .as_mut() .and_then(|m| m.get_mut(&TypeId::of::())) .and_then(|boxed| boxed.downcast_mut()) } /// Remove a value from this `TypeMap`. /// /// If a value of this type exists, it will be returned. pub fn remove(&mut self) -> Option { self.map .as_mut() .and_then(|m| m.remove(&TypeId::of::())) .and_then(|boxed| (boxed as Box).downcast().ok().map(|boxed| *boxed)) } /// Clear the `TypeMap` of all inserted values. #[inline] pub fn clear(&mut self) { self.map = None; } /// Get an entry in the `TypeMap` for in-place manipulation. pub fn entry(&mut self) -> Entry { match self.map.get_or_insert_with(|| FxHashMap::default()).entry(TypeId::of::()) { hash_map::Entry::Occupied(e) => { Entry::Occupied(OccupiedEntry { data: e, marker: PhantomData }) } hash_map::Entry::Vacant(e) => { Entry::Vacant(VacantEntry { data: e, marker: PhantomData }) } } } } } #[test] fn test_type_map() { #[derive(Debug, PartialEq)] struct MyType(i32); #[derive(Debug, PartialEq, Default)] struct MyType2(String); let mut map = TypeMap::new(); map.insert(5i32); map.insert(MyType(10)); assert_eq!(map.get(), Some(&5i32)); assert_eq!(map.get_mut(), Some(&mut 5i32)); assert_eq!(map.remove::(), Some(5i32)); assert!(map.get::().is_none()); assert_eq!(map.get::(), None); assert_eq!(map.get(), Some(&MyType(10))); let entry = map.entry::(); let mut v = entry.or_insert_with(MyType2::default); v.0 = "Hello".into(); assert_eq!(map.get(), Some(&MyType2("Hello".into()))); }