Skip to content

Instantly share code, notes, and snippets.

@tesaguri
Last active September 9, 2023 02:00
Show Gist options
  • Save tesaguri/28ececdcd44343e76397091f20ace9f6 to your computer and use it in GitHub Desktop.
Save tesaguri/28ececdcd44343e76397091f20ace9f6 to your computer and use it in GitHub Desktop.
A prrof of concept for a cloneable version of `http::Extensions`
use core::any::TypeId;
use std::collections::HashMap;
#[derive(Default)]
pub struct Extensions {
map: HashMap<TypeId, Box<dyn DynTryClone>>,
}
#[repr(transparent)]
struct Unclone<T>(T);
trait DynTryClone {
fn try_clone_box(&self) -> Option<Box<dyn DynTryClone + 'static>>;
}
impl Extensions {
pub fn new() -> Self {
Self::default()
}
pub fn insert<T: 'static>(&mut self, value: T) -> Option<T> {
self.map
.insert(TypeId::of::<T>(), Box::new(Unclone(value)))
.map(|boxed| *unsafe { Box::from_raw(Box::into_raw(boxed) as *mut T) })
}
pub fn insert_clone<T: Clone + 'static>(&mut self, value: T) -> Option<T> {
self.map
.insert(TypeId::of::<T>(), Box::new(value))
.map(|boxed| *unsafe { Box::from_raw(Box::into_raw(boxed) as *mut T) })
}
pub fn get<T: 'static>(&self) -> Option<&T> {
self.map
.get(&TypeId::of::<T>())
.map(|boxed| unsafe { &*((&**boxed) as *const dyn DynTryClone as *const T) })
}
pub fn get_mut<T: 'static>(&mut self) -> Option<&mut T> {
self.map
.get_mut(&TypeId::of::<T>())
.map(|boxed| unsafe { &mut *((&mut **boxed) as *mut dyn DynTryClone as *mut T) })
}
pub fn remove<T: 'static>(&mut self) -> Option<T> {
self.map
.remove(&TypeId::of::<T>())
.map(|boxed| *unsafe { Box::from_raw(Box::into_raw(boxed) as *mut T) })
}
}
impl Clone for Extensions {
fn clone(&self) -> Self {
let map = self
.map
.iter()
.filter_map(|(&key, value)| (**value).try_clone_box().map(|v| (key, v)))
.collect();
Self { map }
}
}
impl<T: Clone + 'static> DynTryClone for T {
fn try_clone_box(&self) -> Option<Box<dyn DynTryClone>> {
Some(Box::new(self.clone()))
}
}
impl<T> DynTryClone for Unclone<T> {
fn try_clone_box(&self) -> Option<Box<dyn DynTryClone>> {
None
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Uncloneable;
#[test]
fn it_works() {
let mut extensions = Extensions::new();
extensions.insert(Uncloneable);
extensions.insert_clone(());
let cloned = extensions.clone();
assert!(cloned.get::<Uncloneable>().is_none());
assert!(cloned.get::<()>().is_some());
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment