use std::{ any::Any, marker::PhantomData, mem::ManuallyDrop, ops::{Deref, DerefMut}, ptr::NonNull, }; #[repr(transparent)] pub struct OwnRef<'a, T: ?Sized>(Option>, PhantomData<(&'a mut [u8], T)>); impl Drop for OwnRef<'_, T> { fn drop(&mut self) { let Some(p) = self.0 else { return }; unsafe { p.drop_in_place() }; } } impl Deref for OwnRef<'_, T> { type Target = T; fn deref(&self) -> &Self::Target { unsafe { self.0.unwrap().as_ref() } } } impl DerefMut for OwnRef<'_, T> { fn deref_mut(&mut self) -> &mut Self::Target { unsafe { self.0.unwrap().as_mut() } } } impl OwnRef<'_, T> { pub unsafe fn new_unchecked(p: NonNull) -> Self { Self(Some(p), PhantomData) } pub fn into_inner(mut self) -> NonNull { self.0.take().unwrap() } } impl<'a, T: ?Sized> OwnRef<'a, T> { pub unsafe fn from_manually_drop(p: &'a mut ManuallyDrop) -> Self { unsafe { Self::new_unchecked(p.deref_mut().into()) } } pub fn into_manually_drop(self) -> &'a mut ManuallyDrop { let p = self.into_inner().as_ptr() as *mut ManuallyDrop; unsafe { p.as_mut().unwrap() } } } impl OwnRef<'_, T> { pub fn take(mut self) -> T { let p = self.0.take().unwrap(); unsafe { p.read() } } } impl OwnRef<'_, dyn Any> { pub fn take(mut self) -> Result { if !self.is::() { return Err(self); } let p = self.0.take().unwrap(); let p = p.cast::(); Ok(unsafe { p.read() }) } } #[macro_export] macro_rules! _to_ptr_own_ref { ($var:ident) => { $crate::_to_ptr_own_ref!(# $var) }; (mut $var:ident) => { $crate::_to_ptr_own_ref!(mut # $var) }; ($($mut:ident)? # $var:ident) => { let mut $var = ManuallyDrop::new($var); let $($mut)? $var = unsafe { OwnRef::from_manually_drop(&mut $var) }; }; } pub use _to_ptr_own_ref as to_own_ref; #[cfg(test)] mod tests { use super::*; #[test] fn test_ref() { let obj = "some string".to_string(); to_own_ref!(obj); assert_eq!(*obj, "some string"); } #[test] fn test_mut() { let obj = b"some string".to_vec(); to_own_ref!(mut obj); obj[0] = b'm'; obj[1] = b'o'; obj[2] = b'r'; obj[3] = b'e'; assert_eq!(*obj, b"more string"); } #[test] fn test_take() { let obj = "some string".to_string(); to_own_ref!(obj); let s: String = obj.take(); assert_eq!(s, "some string"); } }