use crate::alloc::Box; use core::any::TypeId; use core::marker::PhantomData; use core::mem::{self, MaybeUninit}; use core::ptr; #[cfg(feature = "unstable-debug")] use core::any; pub struct Any { value: Value, drop: unsafe fn(&mut Value), type_id: TypeId, /// For panic messages only. Not used for comparison. #[cfg(feature = "unstable-debug")] type_name: &'static str, } union Value { ptr: *mut (), inline: [MaybeUninit; 2], } fn is_small() -> bool { mem::size_of::() <= mem::size_of::() && mem::align_of::() <= mem::align_of::() } impl Any { // This is unsafe -- caller must not hold on to the Any beyond the lifetime // of T. // // Example of bad code: // // let s = "bad".to_owned(); // let a = Any::new(&s); // drop(s); // // Now `a.view()` and `a.take()` return references to a dead String. pub(crate) unsafe fn new(t: T) -> Self { let value: Value; let drop: unsafe fn(&mut Value); let type_id = non_static_type_id::(); if is_small::() { let mut inline = [MaybeUninit::uninit(); 2]; unsafe { ptr::write(inline.as_mut_ptr().cast::(), t) }; value = Value { inline }; unsafe fn inline_drop(value: &mut Value) { unsafe { ptr::drop_in_place(value.inline.as_mut_ptr().cast::()) } } drop = inline_drop::; } else { let ptr = Box::into_raw(Box::new(t)).cast::<()>(); value = Value { ptr }; unsafe fn ptr_drop(value: &mut Value) { mem::drop(unsafe { Box::from_raw(value.ptr.cast::()) }); } drop = ptr_drop::; }; Any { value, drop, type_id, #[cfg(feature = "unstable-debug")] type_name: any::type_name::(), } } // This is unsafe -- caller is responsible that T is the correct type. pub(crate) unsafe fn view(&mut self) -> &mut T { if self.type_id != non_static_type_id::() { self.invalid_cast_to::(); } let ptr = if is_small::() { unsafe { self.value.inline.as_mut_ptr().cast::() } } else { unsafe { self.value.ptr.cast::() } }; unsafe { &mut *ptr } } // This is unsafe -- caller is responsible that T is the correct type. pub(crate) unsafe fn take(mut self) -> T { if self.type_id != non_static_type_id::() { self.invalid_cast_to::(); } if is_small::() { let ptr = unsafe { self.value.inline.as_mut_ptr().cast::() }; let value = unsafe { ptr::read(ptr) }; mem::forget(self); value } else { let ptr = unsafe { self.value.ptr.cast::() }; let box_t = unsafe { Box::from_raw(ptr) }; mem::forget(self); *box_t } } #[cfg(not(feature = "unstable-debug"))] fn invalid_cast_to(&self) -> ! { panic!("invalid cast; enable `unstable-debug` feature to debug"); } #[cfg(feature = "unstable-debug")] fn invalid_cast_to(&self) -> ! { let from = self.type_name; let to = any::type_name::(); panic!("invalid cast: {} to {}", from, to); } } impl Drop for Any { fn drop(&mut self) { unsafe { (self.drop)(&mut self.value) } } } trait NonStaticAny { fn get_type_id(&self) -> TypeId where Self: 'static; } impl NonStaticAny for PhantomData { fn get_type_id(&self) -> TypeId where Self: 'static, { TypeId::of::() } } fn non_static_type_id() -> TypeId { let non_static_thing = &PhantomData::; let thing = unsafe { mem::transmute::<&dyn NonStaticAny, &(dyn NonStaticAny + 'static)>(non_static_thing) }; NonStaticAny::get_type_id(thing) } #[test] fn test_non_static_type_id() { assert_eq!(non_static_type_id::(), non_static_type_id::()); assert_eq!( non_static_type_id::<&str>(), non_static_type_id::<&'static str>() ); assert_ne!(non_static_type_id::(), non_static_type_id::<[u8; 4]>()); assert_ne!( non_static_type_id::(), non_static_type_id::<[u32; 2]>() ); assert_ne!(non_static_type_id::(), non_static_type_id::()); assert_ne!( non_static_type_id::(), non_static_type_id::<&usize>() ); assert_ne!( non_static_type_id::<&usize>(), non_static_type_id::<&&usize>() ); assert_ne!( non_static_type_id::<&usize>(), non_static_type_id::<&mut usize>() ); struct A; struct B; assert_ne!(non_static_type_id::(), non_static_type_id::()); }