any_container/map.rs
1use std::any::{Any, TypeId};
2use std::collections::HashMap;
3use std::collections::hash_map::Entry;
4use std::fmt;
5use std::hash::BuildHasherDefault;
6
7use crate::boxed::AnyCloneBox;
8use crate::utils::IdHasher;
9
10/// A type-erased map storing values of different types by their `TypeId`.
11///
12/// `AnyMap` allows storing and retrieving values of any type that implements
13/// `Clone + Send + Sync`. The map uses `TypeId` as keys, enabling type-safe
14/// lookups while maintaining type erasure through boxed trait objects.
15///
16/// # Examples
17///
18/// ```
19/// use any_container::AnyMap;
20///
21/// let mut map = AnyMap::new();
22/// map.insert(42i32);
23/// map.insert("world".to_string());
24///
25/// assert_eq!(map.len(), 2);
26/// assert_eq!(*map.get::<i32>().unwrap(), 42);
27/// ```
28#[repr(transparent)]
29#[derive(Clone)]
30pub struct AnyMap {
31 // A map from a TypeId to a Box of a type
32 map: HashMap<TypeId, AnyCloneBox, BuildHasherDefault<IdHasher>>,
33}
34
35impl Default for AnyMap {
36 fn default() -> Self {
37 Self::new()
38 }
39}
40
41impl AnyMap {
42 /// Creates a new empty `AnyMap`.
43 pub fn new() -> AnyMap {
44 AnyMap {
45 map: HashMap::default(),
46 }
47 }
48
49 /// Creates a new empty `AnyMap` with the specified capacity.
50 pub fn with_capacity(capacity: usize) -> AnyMap {
51 AnyMap {
52 map: HashMap::with_capacity_and_hasher(
53 capacity,
54 BuildHasherDefault::<IdHasher>::default(),
55 ),
56 }
57 }
58
59 /// Returns the number of elements that the map can hold without reallocating.
60 pub fn capacity(&self) -> usize {
61 self.map.capacity()
62 }
63
64 /// Reserves capacity for at least `additional` more elements in the map.
65 /// The map may reserve more space than requested to avoid frequent reallocations.
66 pub fn reserve(&mut self, additional: usize) {
67 self.map.reserve(additional);
68 }
69
70 /// Shrinks the capacity of the map as much as possible.
71 /// This reduces the allocated memory to fit the current contents.
72 pub fn shrink_to_fit(&mut self) {
73 self.map.shrink_to_fit();
74 }
75
76 /// Returns the number of elements in the map.
77 pub fn len(&self) -> usize {
78 self.map.len()
79 }
80
81 /// Returns true if the map contains no elements.
82 pub fn is_empty(&self) -> bool {
83 self.map.is_empty()
84 }
85
86 /// Removes all elements from the map.
87 pub fn clear(&mut self) {
88 self.map.clear();
89 }
90
91 /// Returns a reference to the value corresponding to the type `T`.
92 pub fn get<T: Any>(&self) -> Option<&T> {
93 self.map.get(&TypeId::of::<T>()).map(|elem| unsafe {
94 debug_assert_eq!(
95 AnyCloneBox::type_id(elem),
96 TypeId::of::<T>(),
97 "TypeId mismatch in AnyMap::get. This should never happen!"
98 );
99 // Safety: The invariants guarantee that the type of the value is the same as the type of the key.
100 elem.downcast_ref_unchecked()
101 })
102 }
103
104 /// Returns a mutable reference to the value corresponding to the type `T`.
105 pub fn get_mut<T: Any>(&mut self) -> Option<&mut T> {
106 self.map.get_mut(&TypeId::of::<T>()).map(|elem| unsafe {
107 debug_assert_eq!(
108 AnyCloneBox::type_id(elem),
109 TypeId::of::<T>(),
110 "TypeId mismatch in AnyMap::get_mut. This should never happen!"
111 );
112 // Safety: The invariants guarantee that the type of the value is the same as the type of the key.
113 elem.downcast_mut_unchecked()
114 })
115 }
116
117 // Inserts a value of type `T` into the map. If a value of type `T` already exists, it will be
118 // replaced and the old value will be returned.
119 pub fn insert<T: Any + Clone + Send + Sync>(&mut self, value: T) -> Option<T> {
120 self.map
121 .insert(TypeId::of::<T>(), AnyCloneBox::new(value))
122 .map(|old_value| unsafe {
123 debug_assert_eq!(
124 AnyCloneBox::type_id(&old_value),
125 TypeId::of::<T>(),
126 "TypeId mismatch in AnyMap::insert. This should never happen!"
127 );
128 // Safety: The invariants guarantee that the type of the value is the same as the type of the key.
129 *old_value.downcast_unchecked()
130 })
131 }
132
133 /// Inserts a boxed value into the map.
134 /// If a value of the same `TypeId` already exists, it will be replaced and the old value
135 /// will be returned.
136 /// This method is useful when you already have an `AnyCloneBox` instance.
137 pub fn insert_boxed(&mut self, value: AnyCloneBox) -> Option<AnyCloneBox> {
138 self.map.insert(value.type_id(), value)
139 }
140
141 /// Inserts a value of type `T` into the map. If a value of type `T` already exists,
142 /// it will not be replaced and the new value will be returned as an error.
143 pub fn try_insert<T: Any + Clone + Send + Sync>(&mut self, value: T) -> Result<(), T> {
144 match self.map.entry(TypeId::of::<T>()) {
145 Entry::Occupied(_) => Err(value),
146 Entry::Vacant(entry) => {
147 entry.insert(AnyCloneBox::new(value));
148 Ok(())
149 }
150 }
151 }
152
153 /// Inserts a boxed value into the map. If a value of the same type already exists,
154 /// it will not be replaced and the new value will be returned as an error.
155 pub fn try_insert_boxed(&mut self, value: AnyCloneBox) -> Result<(), AnyCloneBox> {
156 match self.map.entry(value.type_id()) {
157 Entry::Occupied(_) => Err(value),
158 Entry::Vacant(entry) => {
159 entry.insert(value);
160 Ok(())
161 }
162 }
163 }
164
165 /// Removes the stored value of type `T` from the map and returns it, if it exists.
166 pub fn remove<T: Any>(&mut self) -> Option<T> {
167 self.map.remove(&TypeId::of::<T>()).map(|old_value| unsafe {
168 debug_assert_eq!(
169 AnyCloneBox::type_id(&old_value),
170 TypeId::of::<T>(),
171 "TypeId mismatch in AnyMap::remove. This should never happen!"
172 );
173 // Safety: The invariants guarantee that the type of the value is the same as the type of the key.
174 *old_value.downcast_unchecked()
175 })
176 }
177
178 /// Returns true if the map contains a value of type `T`.
179 pub fn contains<T: Any>(&self) -> bool {
180 self.map.contains_key(&TypeId::of::<T>())
181 }
182}
183
184impl fmt::Debug for AnyMap {
185 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
186 f.debug_set()
187 .entries(self.map.values().map(|any_clone| any_clone.type_name()))
188 .finish()
189 }
190}