use std::any::TypeId;
#[derive(Debug)]
pub struct OwnedPtr<T: ?Sized> {
data: Box<T>,
concrete_type_id: TypeId,
}
impl<T: Sized + 'static> OwnedPtr<T> {
pub fn new(value: T) -> Self {
OwnedPtr {
data: Box::new(value),
concrete_type_id: TypeId::of::<T>(),
}
}
}
impl<T: ?Sized> OwnedPtr<T> {
#[allow(clippy::should_implement_trait)]
pub fn borrow(&self) -> &T {
&self.data
}
#[allow(clippy::should_implement_trait)]
pub unsafe fn borrow_mut(&mut self) -> &mut T {
&mut self.data
}
pub fn downgrade(&self) -> WeakPtr<T> {
WeakPtr {
data: Some(&*self.data),
concrete_type_id: self.concrete_type_id,
}
}
pub fn downcast<U: 'static>(self) -> Result<OwnedPtr<U>, OwnedPtr<T>> {
if self.concrete_type_id == TypeId::of::<U>() {
let inner = Box::into_raw(self.data);
let converted = unsafe { Box::from_raw(inner as *mut U) };
Ok(OwnedPtr::from_inner((converted, self.concrete_type_id)))
} else {
Err(self)
}
}
pub fn from_inner(inner: (Box<T>, TypeId)) -> Self {
OwnedPtr {
data: inner.0,
concrete_type_id: inner.1,
}
}
pub fn into_inner(self) -> (Box<T>, TypeId) {
(self.data, self.concrete_type_id)
}
}
#[derive(Debug)]
pub struct WeakPtr<T: ?Sized> {
data: Option<*const T>,
concrete_type_id: TypeId,
}
impl<T: ?Sized + 'static> WeakPtr<T> {
pub fn create_uninitialized() -> Self {
WeakPtr {
data: None,
concrete_type_id: TypeId::of::<T>(),
}
}
}
impl<T: ?Sized> WeakPtr<T> {
pub fn is_initialized(&self) -> bool {
self.data.is_some()
}
#[allow(clippy::should_implement_trait)]
pub fn borrow(&self) -> &T {
unsafe { &*self.data.unwrap() }
}
pub fn downcast<U: 'static>(self) -> Result<WeakPtr<U>, WeakPtr<T>> {
if self.concrete_type_id == TypeId::of::<U>() {
let converted = self.data.map(|ptr| ptr as *const U);
Ok(WeakPtr::from_inner((converted, self.concrete_type_id)))
} else {
Err(self)
}
}
pub fn from_inner(inner: (Option<*const T>, TypeId)) -> Self {
WeakPtr {
data: inner.0,
concrete_type_id: inner.1,
}
}
pub fn into_inner(self) -> (Option<*const T>, TypeId) {
(self.data, self.concrete_type_id)
}
}
impl<T: ?Sized> Clone for WeakPtr<T> {
fn clone(&self) -> Self {
WeakPtr {
data: self.data,
concrete_type_id: self.concrete_type_id,
}
}
}
unsafe impl<T: ?Sized + Send> Send for OwnedPtr<T> {}
unsafe impl<T: ?Sized + Sync> Sync for OwnedPtr<T> {}
unsafe impl<T: ?Sized + Send> Send for WeakPtr<T> {}
unsafe impl<T: ?Sized + Sync> Sync for WeakPtr<T> {}
#[macro_export]
macro_rules! downgrade_as {
($owned:expr, $new_type:ty) => {
$crate::upcast_weak_as!($owned.downgrade(), $new_type)
};
}
#[macro_export]
macro_rules! upcast_owned_as {
($owned:expr, $new_type:ty) => {{
let (data, type_id) = $owned.into_inner();
OwnedPtr::from_inner((data as Box<$new_type>, type_id))
}};
}
#[macro_export]
macro_rules! upcast_weak_as {
($weak:expr, $new_type:ty) => {{
let (data, type_id) = $weak.into_inner();
WeakPtr::from_inner((data.map(|ptr| ptr as *const $new_type), type_id))
}};
}
impl<'a, T: ?Sized, U: ?Sized> PartialEq<&'a T> for OwnedPtr<U> {
fn eq(&self, other: &&'a T) -> bool {
let self_ptr = (&*self.data as *const U).cast::<()>();
let other_ptr = (*other as *const T).cast::<()>();
std::ptr::eq(self_ptr, other_ptr)
}
}
impl<'a, T: ?Sized, U: ?Sized> PartialEq<&'a T> for WeakPtr<U> {
fn eq(&self, other: &&'a T) -> bool {
let Some(self_ptr) = self.data else { return false };
let other_ptr = (*other as *const T).cast::<()>();
std::ptr::eq(self_ptr.cast::<()>(), other_ptr)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn multiple_immutable_borrows_is_legal() {
let owned_ptr = OwnedPtr::new(79_u32);
let weak_ptr1 = owned_ptr.downgrade();
let weak_ptr2 = owned_ptr.downgrade();
let borrow0 = owned_ptr.borrow();
let borrow1 = weak_ptr1.borrow();
let borrow2 = weak_ptr2.borrow();
let dummy = borrow0 + borrow1 + borrow2;
assert_eq!(dummy, 3 * 79);
}
#[test]
#[should_panic]
fn accessing_uninitialized_pointer_causes_panic() {
let weak_ptr: WeakPtr<String> = WeakPtr::create_uninitialized();
weak_ptr.borrow();
}
#[test]
fn pointer_equality_is_reflexive() {
let owned_ptr = OwnedPtr::new("test".to_owned());
let weak_ptr = owned_ptr.downgrade();
assert_eq!(owned_ptr, owned_ptr.borrow());
assert_eq!(owned_ptr, weak_ptr.borrow());
assert_eq!(weak_ptr, owned_ptr.borrow());
assert_eq!(weak_ptr, weak_ptr.borrow());
}
#[test]
fn pointer_equality_is_type_independent() {
let owned_ptr: OwnedPtr<String> = OwnedPtr::new("test".to_owned());
let weak_ptr: WeakPtr<String> = owned_ptr.downgrade();
let (raw_pointer, type_id) = weak_ptr.into_inner();
let casted_pointer = raw_pointer.map(|ptr| ptr as *const bool);
let casted_weak_ptr: WeakPtr<bool> = WeakPtr::from_inner((casted_pointer, type_id));
assert_eq!(casted_weak_ptr, owned_ptr.borrow());
}
#[test]
fn different_pointers_are_not_equal() {
let owned_ptr1 = OwnedPtr::new(79_i32);
let weak_ptr1 = owned_ptr1.downgrade();
let owned_ptr2 = OwnedPtr::new(79_i32);
let weak_ptr2 = owned_ptr2.downgrade();
assert_ne!(owned_ptr1, owned_ptr2.borrow());
assert_ne!(owned_ptr1, weak_ptr2.borrow());
assert_ne!(weak_ptr1, owned_ptr2.borrow());
assert_ne!(weak_ptr1, weak_ptr2.borrow());
assert_ne!(owned_ptr2, owned_ptr1.borrow());
assert_ne!(owned_ptr2, weak_ptr1.borrow());
assert_ne!(weak_ptr2, owned_ptr1.borrow());
assert_ne!(weak_ptr2, weak_ptr1.borrow());
}
}