114 lines
2.3 KiB
Rust
114 lines
2.3 KiB
Rust
use std::{
|
|
any::Any,
|
|
marker::PhantomData,
|
|
mem::ManuallyDrop,
|
|
ops::{Deref, DerefMut},
|
|
ptr::NonNull,
|
|
};
|
|
|
|
#[repr(transparent)]
|
|
pub struct OwnRef<'a, T: ?Sized>(Option<NonNull<T>>, PhantomData<(&'a mut [u8], T)>);
|
|
|
|
impl<T: ?Sized> Drop for OwnRef<'_, T> {
|
|
fn drop(&mut self) {
|
|
let Some(p) = self.0 else { return };
|
|
unsafe { p.drop_in_place() };
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> Deref for OwnRef<'_, T> {
|
|
type Target = T;
|
|
fn deref(&self) -> &Self::Target {
|
|
unsafe { self.0.unwrap().as_ref() }
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> DerefMut for OwnRef<'_, T> {
|
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
unsafe { self.0.unwrap().as_mut() }
|
|
}
|
|
}
|
|
|
|
impl<T: ?Sized> OwnRef<'_, T> {
|
|
pub unsafe fn new_unchecked(p: NonNull<T>) -> Self {
|
|
Self(Some(p), PhantomData)
|
|
}
|
|
|
|
pub fn into_inner(mut self) -> NonNull<T> {
|
|
self.0.take().unwrap()
|
|
}
|
|
}
|
|
|
|
impl<'a, T: ?Sized> OwnRef<'a, T> {
|
|
pub unsafe fn from_manually_drop(p: &'a mut ManuallyDrop<T>) -> Self {
|
|
unsafe { Self::new_unchecked(p.deref_mut().into()) }
|
|
}
|
|
|
|
pub fn into_manually_drop(self) -> &'a mut ManuallyDrop<T> {
|
|
let p = self.into_inner().as_ptr() as *mut ManuallyDrop<T>;
|
|
unsafe { p.as_mut().unwrap() }
|
|
}
|
|
}
|
|
|
|
impl<T> OwnRef<'_, T> {
|
|
pub fn take(mut self) -> T {
|
|
let p = self.0.take().unwrap();
|
|
unsafe { p.read() }
|
|
}
|
|
}
|
|
|
|
impl OwnRef<'_, dyn Any> {
|
|
pub fn take<U: 'static>(mut self) -> Result<U, Self> {
|
|
if !self.is::<U>() {
|
|
return Err(self);
|
|
}
|
|
let p = self.0.take().unwrap();
|
|
let p = p.cast::<U>();
|
|
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");
|
|
}
|
|
}
|