You can get something similar to this with a manual HashMap<TypeId, ...>
, as long as T: 'static
:
use once_cell::sync::Lazy;
use std::{
any::{Any, TypeId},
collections::{hash_map::Entry::*, HashMap},
sync::{Arc, RwLock},
};
fn new_empty<T>() -> Arc<T>
where
T: 'static + Send + Sync + Default,
{
static EMPTIES: Lazy<RwLock<HashMap<TypeId, Box<dyn Any + Send + Sync>>>> = Lazy::new(Default::default);
let e = EMPTIES
.read()
.unwrap()
.get(&TypeId::of::<T>())
.map(|arc| arc.downcast_ref::<Arc<T>>().unwrap().clone());
e.unwrap_or_else(|| match EMPTIES.write().unwrap().entry(TypeId::of::<T>()) {
Occupied(o) => o.get().downcast_ref::<Arc<T>>().unwrap().clone(),
Vacant(v) => {
let i = <Box<Arc<T>>>::default();
let r = Arc::clone(&i);
v.insert(i);
r
}
})
}
fn main() {
let a: Arc<i32> = new_empty();
let b: Arc<u8> = new_empty();
let c: Arc<()> = new_empty();
let d: Arc<i32> = new_empty();
let e: Arc<i32> = new_empty();
println!("{0:?} {0:p}\n{1:?} {1:p}\n{2:?} {2:p}\n{3:?} {3:p}\n{4:?} {4:p}", a, b, c, d, e);
}
(playground)
Edit: One step of indirection and allocation can be avoided by using SmallBox
(click here for an example)
use default::default;
use once_cell::sync::Lazy;
use smallbox::{smallbox, space::S2, SmallBox};
use std::{
any::{Any, TypeId},
collections::HashMap,
sync::{Arc, RwLock},
};
fn new_empty<T>() -> Arc<T>
where
T: 'static + Send + Sync + Default,
{
static EMPTIES: Lazy<RwLock<HashMap<TypeId, SmallBox<dyn Any + Send + Sync, S2>>>> =
Lazy::new(default);
let cast =
|r: &SmallBox<dyn Any + Send + Sync, S2>| r.downcast_ref::<Arc<T>>().unwrap().clone();
let option = EMPTIES.read().unwrap().get(&TypeId::of::<T>()).map(cast);
option.unwrap_or_else(|| {
cast(
EMPTIES
.write()
.unwrap()
.entry(TypeId::of::<T>())
.or_insert(smallbox!(Arc::<T>::default())),
)
})
}
Edit2: Make sure not to miss this: