From c65116c173b2e8c536a4e594bd482a8f2d54e252 Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Mon, 16 Oct 2023 13:03:37 -0500 Subject: [PATCH] restore deref Ref> --- packages/generational-box/src/lib.rs | 8 ++-- packages/signals/src/rt.rs | 42 +++++++++++++++-- packages/signals/src/signal.rs | 69 ++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+), 7 deletions(-) diff --git a/packages/generational-box/src/lib.rs b/packages/generational-box/src/lib.rs index 60bd41419..4be383b4f 100644 --- a/packages/generational-box/src/lib.rs +++ b/packages/generational-box/src/lib.rs @@ -184,7 +184,7 @@ impl GenerationalBox { } /// Try to read the value. Returns None if the value is no longer valid. - pub fn try_read(&self) -> Option> { + pub fn try_read(&self) -> Option> { self.validate() .then(|| { Ref::filter_map(self.raw.data.borrow(), |any| { @@ -196,12 +196,12 @@ impl GenerationalBox { } /// Read the value. Panics if the value is no longer valid. - pub fn read(&self) -> Ref<'_, T> { + pub fn read(&self) -> Ref<'static, T> { self.try_read().unwrap() } /// Try to write the value. Returns None if the value is no longer valid. - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { self.validate() .then(|| { RefMut::filter_map(self.raw.data.borrow_mut(), |any| { @@ -213,7 +213,7 @@ impl GenerationalBox { } /// Write the value. Panics if the value is no longer valid. - pub fn write(&self) -> RefMut<'_, T> { + pub fn write(&self) -> RefMut<'static, T> { self.try_write().unwrap() } diff --git a/packages/signals/src/rt.rs b/packages/signals/src/rt.rs index 113fe9b30..ac7915531 100644 --- a/packages/signals/src/rt.rs +++ b/packages/signals/src/rt.rs @@ -1,5 +1,7 @@ use std::cell::{Ref, RefMut}; +use std::mem::MaybeUninit; +use std::ops::Deref; use std::rc::Rc; use dioxus_core::prelude::*; @@ -120,17 +122,17 @@ impl CopyValue { } /// Read the value. If the value has been dropped, this will panic. - pub fn read(&self) -> Ref<'_, T> { + pub fn read(&self) -> Ref<'static, T> { self.value.read() } /// Try to write the value. If the value has been dropped, this will return None. - pub fn try_write(&self) -> Option> { + pub fn try_write(&self) -> Option> { self.value.try_write() } /// Write the value. If the value has been dropped, this will panic. - pub fn write(&self) -> RefMut<'_, T> { + pub fn write(&self) -> RefMut<'static, T> { self.value.write() } @@ -164,3 +166,37 @@ impl PartialEq for CopyValue { self.value.ptr_eq(&other.value) } } + +impl Deref for CopyValue { + type Target = dyn Fn() -> Ref<'static, T>; + + fn deref(&self) -> &Self::Target { + // https://github.com/dtolnay/case-studies/tree/master/callable-types + + // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit). + let uninit_callable = MaybeUninit::::uninit(); + // Then move that value into the closure. We assume that the closure now has a in memory layout of Self. + let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }); + + // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure. + let size_of_closure = std::mem::size_of_val(&uninit_closure); + assert_eq!(size_of_closure, std::mem::size_of::()); + + // Then cast the lifetime of the closure to the lifetime of &self. + fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T { + b + } + let reference_to_closure = cast_lifetime( + { + // The real closure that we will never use. + &uninit_closure + }, + // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self. + unsafe { std::mem::transmute(self) }, + ); + + // Cast the closure to a trait object. + let reference_to_trait_object = reference_to_closure as &Self::Target; + reference_to_trait_object + } +} diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs index 91619e0fb..e5b521085 100644 --- a/packages/signals/src/signal.rs +++ b/packages/signals/src/signal.rs @@ -1,5 +1,6 @@ use std::{ cell::{Ref, RefCell, RefMut}, + mem::MaybeUninit, ops::{Deref, DerefMut}, rc::Rc, sync::Arc, @@ -268,6 +269,40 @@ impl PartialEq for Signal { } } +impl Deref for Signal { + type Target = dyn Fn() -> Ref<'static, T>; + + fn deref(&self) -> &Self::Target { + // https://github.com/dtolnay/case-studies/tree/master/callable-types + + // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit). + let uninit_callable = MaybeUninit::::uninit(); + // Then move that value into the closure. We assume that the closure now has a in memory layout of Self. + let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }); + + // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure. + let size_of_closure = std::mem::size_of_val(&uninit_closure); + assert_eq!(size_of_closure, std::mem::size_of::()); + + // Then cast the lifetime of the closure to the lifetime of &self. + fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T { + b + } + let reference_to_closure = cast_lifetime( + { + // The real closure that we will never use. + &uninit_closure + }, + // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self. + unsafe { std::mem::transmute(self) }, + ); + + // Cast the closure to a trait object. + let reference_to_trait_object = reference_to_closure as &Self::Target; + reference_to_trait_object + } +} + struct SignalSubscriberDrop { signal: Signal, } @@ -358,3 +393,37 @@ impl PartialEq for ReadOnlySignal { self.inner == other.inner } } + +impl Deref for ReadOnlySignal { + type Target = dyn Fn() -> Ref<'static, T>; + + fn deref(&self) -> &Self::Target { + // https://github.com/dtolnay/case-studies/tree/master/callable-types + + // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit). + let uninit_callable = MaybeUninit::::uninit(); + // Then move that value into the closure. We assume that the closure now has a in memory layout of Self. + let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }); + + // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure. + let size_of_closure = std::mem::size_of_val(&uninit_closure); + assert_eq!(size_of_closure, std::mem::size_of::()); + + // Then cast the lifetime of the closure to the lifetime of &self. + fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T { + b + } + let reference_to_closure = cast_lifetime( + { + // The real closure that we will never use. + &uninit_closure + }, + // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self. + unsafe { std::mem::transmute(self) }, + ); + + // Cast the closure to a trait object. + let reference_to_trait_object = reference_to_closure as &Self::Target; + reference_to_trait_object + } +}