Merge pull request #1550 from ealmloff/restore-deref-fn-signal
Make signal callable on stable
This commit is contained in:
4 files changed
+133
-7
No files matched your search
@@ -184,7 +184,7 @@ impl<T: 'static> GenerationalBox<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Try to read the value. Returns None if the value is no longer valid.
|
/// Try to read the value. Returns None if the value is no longer valid.
|
||||||
pub fn try_read(&self) -> Option<Ref<'_, T>> {
|
pub fn try_read(&self) -> Option<Ref<'static, T>> {
|
||||||
self.validate()
|
self.validate()
|
||||||
.then(|| {
|
.then(|| {
|
||||||
Ref::filter_map(self.raw.data.borrow(), |any| {
|
Ref::filter_map(self.raw.data.borrow(), |any| {
|
||||||
@@ -196,12 +196,12 @@ impl<T: 'static> GenerationalBox<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read the value. Panics if the value is no longer valid.
|
/// 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()
|
self.try_read().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to write the value. Returns None if the value is no longer valid.
|
/// Try to write the value. Returns None if the value is no longer valid.
|
||||||
pub fn try_write(&self) -> Option<RefMut<'_, T>> {
|
pub fn try_write(&self) -> Option<RefMut<'static, T>> {
|
||||||
self.validate()
|
self.validate()
|
||||||
.then(|| {
|
.then(|| {
|
||||||
RefMut::filter_map(self.raw.data.borrow_mut(), |any| {
|
RefMut::filter_map(self.raw.data.borrow_mut(), |any| {
|
||||||
@@ -213,7 +213,7 @@ impl<T: 'static> GenerationalBox<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Write the value. Panics if the value is no longer valid.
|
/// 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()
|
self.try_write().unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
use std::cell::{Ref, RefMut};
|
use std::cell::{Ref, RefMut};
|
||||||
|
|
||||||
|
use std::mem::MaybeUninit;
|
||||||
|
use std::ops::Deref;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use dioxus_core::prelude::*;
|
use dioxus_core::prelude::*;
|
||||||
@@ -120,17 +122,17 @@ impl<T: 'static> CopyValue<T> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Read the value. If the value has been dropped, this will panic.
|
/// 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()
|
self.value.read()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Try to write the value. If the value has been dropped, this will return None.
|
/// Try to write the value. If the value has been dropped, this will return None.
|
||||||
pub fn try_write(&self) -> Option<RefMut<'_, T>> {
|
pub fn try_write(&self) -> Option<RefMut<'static, T>> {
|
||||||
self.value.try_write()
|
self.value.try_write()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Write the value. If the value has been dropped, this will panic.
|
/// 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()
|
self.value.write()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -164,3 +166,36 @@ impl<T: 'static> PartialEq for CopyValue<T> {
|
|||||||
self.value.ptr_eq(&other.value)
|
self.value.ptr_eq(&other.value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for CopyValue<T> {
|
||||||
|
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<Self>).
|
||||||
|
let uninit_callable = MaybeUninit::<Self>::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::<Self>());
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,6 @@
|
|||||||
use std::{
|
use std::{
|
||||||
cell::{Ref, RefCell, RefMut},
|
cell::{Ref, RefCell, RefMut},
|
||||||
|
mem::MaybeUninit,
|
||||||
ops::{Deref, DerefMut},
|
ops::{Deref, DerefMut},
|
||||||
rc::Rc,
|
rc::Rc,
|
||||||
sync::Arc,
|
sync::Arc,
|
||||||
@@ -268,6 +269,39 @@ impl<T: 'static> PartialEq for Signal<T> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for Signal<T> {
|
||||||
|
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<Self>).
|
||||||
|
let uninit_callable = MaybeUninit::<Self>::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::<Self>());
|
||||||
|
|
||||||
|
// 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<T: 'static> {
|
struct SignalSubscriberDrop<T: 'static> {
|
||||||
signal: Signal<T>,
|
signal: Signal<T>,
|
||||||
}
|
}
|
||||||
@@ -358,3 +392,36 @@ impl<T: 'static> PartialEq for ReadOnlySignal<T> {
|
|||||||
self.inner == other.inner
|
self.inner == other.inner
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<T> Deref for ReadOnlySignal<T> {
|
||||||
|
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<Self>).
|
||||||
|
let uninit_callable = MaybeUninit::<Self>::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::<Self>());
|
||||||
|
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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]
|
#[test]
|
||||||
fn drop_signals() {
|
fn drop_signals() {
|
||||||
let mut dom = VirtualDom::new(|cx| {
|
let mut dom = VirtualDom::new(|cx| {
|
||||||
|
|||||||
Reference in new issue
Block a user