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..0f89a3fe6 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,36 @@ 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. + reference_to_closure as &Self::Target + } +} diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs index 91619e0fb..944bde225 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,39 @@ 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. + reference_to_closure as &Self::Target + } +} + struct SignalSubscriberDrop { signal: Signal, } @@ -358,3 +392,36 @@ 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. + reference_to_closure as &Self::Target + } +} diff --git a/packages/signals/tests/create.rs b/packages/signals/tests/create.rs index 57d8325af..ecda2f376 100644 --- a/packages/signals/tests/create.rs +++ b/packages/signals/tests/create.rs @@ -29,6 +29,30 @@ fn create_signals_global() { } } +#[test] +fn deref_signal() { + let mut dom = VirtualDom::new(|cx| { + render! { + for _ in 0..10 { + Child {} + } + } + }); + + fn Child(cx: Scope) -> Element { + let signal = Signal::new("hello world".to_string()); + + // You can call signals like functions to get a Ref of their value. + assert_eq!(&*signal(), "hello world"); + + render! { + "hello world" + } + } + + let _edits = dom.rebuild().santize(); +} + #[test] fn drop_signals() { let mut dom = VirtualDom::new(|cx| {