Browse Source

Move to a generic GlobalLazy<T> (#2851)

* Expose a generic lazy type

* Switch to generic lazy globals

* simplify global lazy context a bit

* rename LazyGlobal to Global

* use Memo::global in more examples

* Fix soundness issue with deref_impl. It relies on the size of self, so it cannot be safe

* add a comment about safety

* Make clippy happy

* fix formatting

* Restore changes to signal impl

* Add helper methods for global signal and global memo to make getting the inner value easier
Evan Almloff 10 tháng trước cách đây
mục cha
commit
b47a6cf83e

+ 2 - 2
examples/global.rs

@@ -10,7 +10,7 @@ use dioxus::prelude::*;
 const STYLE: &str = asset!("./examples/assets/counter.css");
 
 static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
-static DOUBLED_COUNT: GlobalMemo<i32> = Signal::global_memo(|| COUNT() * 2);
+static DOUBLED_COUNT: GlobalMemo<i32> = Memo::global(|| COUNT() * 2);
 
 fn main() {
     launch(app);
@@ -52,7 +52,7 @@ fn Display() -> Element {
 fn Reset() -> Element {
     // Not all write methods are available on global signals since `write` requires a mutable reference. In these cases,
     // We can simply pull out the actual signal using the signal() method.
-    let mut as_signal = use_hook(|| COUNT.signal());
+    let mut as_signal = use_hook(|| COUNT.resolve());
 
     rsx! {
         button { onclick: move |_| as_signal.set(0), "Reset" }

+ 1 - 1
packages/hooks/src/use_future.rs

@@ -189,6 +189,6 @@ impl Deref for UseFuture {
     type Target = dyn Fn() -> UseFutureState;
 
     fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+        unsafe { Readable::deref_impl(self) }
     }
 }

+ 1 - 1
packages/hooks/src/use_resource.rs

@@ -473,6 +473,6 @@ impl<T: Clone> Deref for Resource<T> {
     type Target = dyn Fn() -> Option<T>;
 
     fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+        unsafe { Readable::deref_impl(self) }
     }
 }

+ 17 - 18
packages/rsx/src/template_body.rs

@@ -167,24 +167,23 @@ impl ToTokens for TemplateBody {
                         }
                     );
 
-                    __template.maybe_with_rt(|__template_read| {
-                        // If the template has not been hot reloaded, we always use the original template
-                        // Templates nested within macros may be merged because they have the same file-line-column-index
-                        // They cannot be hot reloaded, so this prevents incorrect rendering
-                        let __template_read = match __template_read.as_ref() {
-                            Some(__template_read) => __template_read,
-                            None => __original_template(),
-                        };
-                        let mut __dynamic_literal_pool = dioxus_core::internal::DynamicLiteralPool::new(
-                            vec![ #( #dynamic_text.to_string() ),* ],
-                        );
-                        let mut __dynamic_value_pool = dioxus_core::internal::DynamicValuePool::new(
-                            vec![ #( #dynamic_nodes ),* ],
-                            vec![ #( #dyn_attr_printer ),* ],
-                            __dynamic_literal_pool
-                        );
-                        __dynamic_value_pool.render_with(__template_read)
-                    })
+                    // If the template has not been hot reloaded, we always use the original template
+                    // Templates nested within macros may be merged because they have the same file-line-column-index
+                    // They cannot be hot reloaded, so this prevents incorrect rendering
+                    let __template_read = dioxus_core::Runtime::current().ok().map(|_| __template.read());
+                    let __template_read = match __template_read.as_ref().map(|__template_read| __template_read.as_ref()) {
+                        Some(Some(__template_read)) => &__template_read,
+                        _ => __original_template(),
+                    };
+                    let mut __dynamic_literal_pool = dioxus_core::internal::DynamicLiteralPool::new(
+                        vec![ #( #dynamic_text.to_string() ),* ],
+                    );
+                    let mut __dynamic_value_pool = dioxus_core::internal::DynamicValuePool::new(
+                        vec![ #( #dynamic_nodes ),* ],
+                        vec![ #( #dyn_attr_printer ),* ],
+                        __dynamic_literal_pool
+                    );
+                    __dynamic_value_pool.render_with(__template_read)
                 }
                 #[cfg(not(debug_assertions))]
                 {

+ 1 - 1
packages/signals/src/copy_value.rs

@@ -193,7 +193,7 @@ impl<T: Copy, S: Storage<T>> Deref for CopyValue<T, S> {
     type Target = dyn Fn() -> T;
 
     fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+        unsafe { Readable::deref_impl(self) }
     }
 }
 

+ 15 - 82
packages/signals/src/global/memo.rs

@@ -1,93 +1,26 @@
-use crate::{read::Readable, Memo, ReadableRef};
-use crate::{read_impls, GlobalKey};
-use dioxus_core::prelude::ScopeId;
-use generational_box::{BorrowResult, UnsyncStorage};
-use std::ops::Deref;
+use super::{Global, InitializeFromFunction};
+use crate::read::Readable;
+use crate::read_impls;
+use crate::Memo;
 
-use crate::Signal;
-
-use super::get_global_context;
-
-/// A signal that can be accessed from anywhere in the application and created in a static
-pub struct GlobalMemo<T: 'static> {
-    selector: fn() -> T,
-    key: GlobalKey<'static>,
-}
-
-impl<T: PartialEq + 'static> GlobalMemo<T> {
-    #[track_caller]
-    /// Create a new global signal
-    pub const fn new(selector: fn() -> T) -> GlobalMemo<T>
-    where
-        T: PartialEq,
-    {
-        let key = std::panic::Location::caller();
-        GlobalMemo {
-            selector,
-            key: GlobalKey::new(key),
-        }
-    }
-
-    /// Get the key for this global
-    pub fn key(&self) -> GlobalKey<'static> {
-        self.key.clone()
-    }
-
-    /// Get the signal that backs this global.
-    pub fn memo(&self) -> Memo<T> {
-        let key = self.key();
-
-        let context = get_global_context();
-
-        let read = context.signal.borrow();
-        match read.get(&key) {
-            Some(signal) => *signal.downcast_ref::<Memo<T>>().unwrap(),
-            None => {
-                drop(read);
-                // Constructors are always run in the root scope
-                let signal = ScopeId::ROOT.in_runtime(|| Signal::memo(self.selector));
-                context.signal.borrow_mut().insert(key, Box::new(signal));
-                signal
-            }
-        }
+impl<T: PartialEq> InitializeFromFunction<T> for Memo<T> {
+    fn initialize_from_function(f: fn() -> T) -> Self {
+        Memo::new(f)
     }
+}
 
-    /// Get the scope the signal was created in.
-    pub fn origin_scope(&self) -> ScopeId {
-        ScopeId::ROOT
-    }
+/// A memo that can be accessed from anywhere in the application and created in a static
+pub type GlobalMemo<T> = Global<Memo<T>, T>;
 
+impl<T: PartialEq + 'static> GlobalMemo<T> {
     /// Get the generational id of the signal.
     pub fn id(&self) -> generational_box::GenerationalBoxId {
-        self.memo().id()
-    }
-}
-
-impl<T: PartialEq + 'static> Readable for GlobalMemo<T> {
-    type Target = T;
-    type Storage = UnsyncStorage;
-
-    #[track_caller]
-    fn try_read_unchecked(
-        &self,
-    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
-        self.memo().try_read_unchecked()
-    }
-
-    #[track_caller]
-    fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
-        self.memo().try_peek_unchecked()
+        self.resolve().id()
     }
-}
-
-/// Allow calling a signal with memo() syntax
-///
-/// Currently only limited to copy types, though could probably specialize for string/arc/rc
-impl<T: PartialEq + Clone + 'static> Deref for GlobalMemo<T> {
-    type Target = dyn Fn() -> T;
 
-    fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+    /// Resolve the global memo. This will try to get the existing value from the current virtual dom, and if it doesn't exist, it will create a new one.
+    pub fn memo(&self) -> Memo<T> {
+        self.resolve()
     }
 }
 

+ 183 - 17
packages/signals/src/global/mod.rs

@@ -1,5 +1,6 @@
-use dioxus_core::prelude::{provide_root_context, try_consume_context};
-use std::{any::Any, cell::RefCell, collections::HashMap, panic::Location, rc::Rc};
+use dioxus_core::ScopeId;
+use generational_box::BorrowResult;
+use std::{any::Any, cell::RefCell, collections::HashMap, ops::Deref, panic::Location, rc::Rc};
 
 mod memo;
 pub use memo::*;
@@ -7,12 +8,182 @@ pub use memo::*;
 mod signal;
 pub use signal::*;
 
-use crate::Signal;
+use crate::{Readable, ReadableRef, Signal, Writable, WritableRef};
+
+/// A trait for an item that can be constructed from an initialization function
+pub trait InitializeFromFunction<T> {
+    /// Create an instance of this type from an initialization function
+    fn initialize_from_function(f: fn() -> T) -> Self;
+}
+
+impl<T> InitializeFromFunction<T> for T {
+    fn initialize_from_function(f: fn() -> T) -> Self {
+        f()
+    }
+}
+
+/// A lazy value that is created once per application and can be accessed from anywhere in that application
+pub struct Global<T, R = T> {
+    constructor: fn() -> R,
+    key: GlobalKey<'static>,
+    phantom: std::marker::PhantomData<fn() -> T>,
+}
+
+/// Allow calling a signal with signal() syntax
+///
+/// Currently only limited to copy types, though could probably specialize for string/arc/rc
+impl<T: Clone + 'static, R: Clone + 'static> Deref for Global<T, R>
+where
+    T: Readable<Target = R> + InitializeFromFunction<R>,
+{
+    type Target = dyn Fn() -> R;
+
+    fn deref(&self) -> &Self::Target {
+        unsafe { Readable::deref_impl(self) }
+    }
+}
+
+impl<T: Clone + 'static, R: 'static> Readable for Global<T, R>
+where
+    T: Readable<Target = R> + InitializeFromFunction<R>,
+{
+    type Target = R;
+    type Storage = T::Storage;
+
+    #[track_caller]
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
+        self.resolve().try_read_unchecked()
+    }
+
+    #[track_caller]
+    fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
+        self.resolve().try_peek_unchecked()
+    }
+}
+
+impl<T: Clone + 'static, R: 'static> Writable for Global<T, R>
+where
+    T: Writable<Target = R> + InitializeFromFunction<R>,
+{
+    type Mut<'a, Read: ?Sized + 'static> = T::Mut<'a, Read>;
+
+    fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
+        ref_: Self::Mut<'_, I>,
+        f: F,
+    ) -> Self::Mut<'_, U> {
+        T::map_mut(ref_, f)
+    }
+
+    fn try_map_mut<
+        I: ?Sized + 'static,
+        U: ?Sized + 'static,
+        F: FnOnce(&mut I) -> Option<&mut U>,
+    >(
+        ref_: Self::Mut<'_, I>,
+        f: F,
+    ) -> Option<Self::Mut<'_, U>> {
+        T::try_map_mut(ref_, f)
+    }
+
+    fn downcast_lifetime_mut<'a: 'b, 'b, Read: ?Sized + 'static>(
+        mut_: Self::Mut<'a, Read>,
+    ) -> Self::Mut<'b, Read> {
+        T::downcast_lifetime_mut(mut_)
+    }
+
+    #[track_caller]
+    fn try_write_unchecked(
+        &self,
+    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
+        self.resolve().try_write_unchecked()
+    }
+}
+
+impl<T: Clone + 'static, R: 'static> Global<T, R>
+where
+    T: Writable<Target = R> + InitializeFromFunction<R>,
+{
+    /// Write this value
+    pub fn write(&self) -> T::Mut<'static, R> {
+        self.resolve().try_write_unchecked().unwrap()
+    }
+
+    /// Run a closure with a mutable reference to the signal's value.
+    /// If the signal has been dropped, this will panic.
+    #[track_caller]
+    pub fn with_mut<O>(&self, f: impl FnOnce(&mut R) -> O) -> O {
+        self.resolve().with_mut(f)
+    }
+}
+
+impl<T: Clone + 'static, R> Global<T, R>
+where
+    T: InitializeFromFunction<R>,
+{
+    #[track_caller]
+    /// Create a new global value
+    pub const fn new(constructor: fn() -> R) -> Self {
+        let key = std::panic::Location::caller();
+        Self {
+            constructor,
+            key: GlobalKey::new(key),
+            phantom: std::marker::PhantomData,
+        }
+    }
+
+    /// Create this global signal with a specific key.
+    /// This is useful for ensuring that the signal is unique across the application and accessible from
+    /// outside the application too.
+    #[track_caller]
+    pub const fn with_key(constructor: fn() -> R, key: &'static str) -> Self {
+        Self {
+            constructor,
+            key: GlobalKey::new_from_str(key),
+            phantom: std::marker::PhantomData,
+        }
+    }
+
+    /// Get the key for this global
+    pub fn key(&self) -> GlobalKey<'static> {
+        self.key.clone()
+    }
+
+    /// Resolve the global value. This will try to get the existing value from the current virtual dom, and if it doesn't exist, it will create a new one.
+    // NOTE: This is not called "get" or "value" because those methods overlap with Readable and Writable
+    pub fn resolve(&self) -> T {
+        let key = self.key();
+
+        let context = get_global_context();
+
+        // Get the entry if it already exists
+        {
+            let read = context.map.borrow();
+            if let Some(signal) = read.get(&key) {
+                return signal.downcast_ref::<T>().cloned().unwrap();
+            }
+        }
+        // Otherwise, create it
+        // Constructors are always run in the root scope
+        let signal = ScopeId::ROOT.in_runtime(|| T::initialize_from_function(self.constructor));
+        context
+            .map
+            .borrow_mut()
+            .insert(key, Box::new(signal.clone()));
+        signal
+    }
+
+    /// Get the scope the signal was created in.
+    pub fn origin_scope(&self) -> ScopeId {
+        ScopeId::ROOT
+    }
+}
 
 /// The context for global signals
-#[derive(Clone)]
-pub struct GlobalSignalContext {
-    signal: Rc<RefCell<HashMap<GlobalKey<'static>, Box<dyn Any>>>>,
+#[derive(Clone, Default)]
+pub struct GlobalLazyContext {
+    map: Rc<RefCell<HashMap<GlobalKey<'static>, Box<dyn Any>>>>,
 }
 
 /// A key used to identify a signal in the global signal context
@@ -57,18 +228,18 @@ impl From<&'static Location<'static>> for GlobalKey<'static> {
     }
 }
 
-impl GlobalSignalContext {
+impl GlobalLazyContext {
     /// Get a signal with the given string key
     /// The key will be converted to a UUID with the appropriate internal namespace
     pub fn get_signal_with_key<T>(&self, key: &str) -> Option<Signal<T>> {
         let key = GlobalKey::new_from_str(key);
 
-        self.signal.borrow().get(&key).map(|f| {
+        self.map.borrow().get(&key).map(|f| {
             *f.downcast_ref::<Signal<T>>().unwrap_or_else(|| {
                 panic!(
                     "Global signal with key {:?} is not of the expected type. Keys are {:?}",
                     key,
-                    self.signal.borrow().keys()
+                    self.map.borrow().keys()
                 )
             })
         })
@@ -76,15 +247,10 @@ impl GlobalSignalContext {
 }
 
 /// Get the global context for signals
-pub fn get_global_context() -> GlobalSignalContext {
-    match try_consume_context() {
+pub fn get_global_context() -> GlobalLazyContext {
+    match ScopeId::ROOT.has_context() {
         Some(context) => context,
-        None => {
-            let context = GlobalSignalContext {
-                signal: Rc::new(RefCell::new(HashMap::new())),
-            };
-            provide_root_context(context)
-        }
+        None => ScopeId::ROOT.provide_context(Default::default()),
     }
 }
 

+ 13 - 158
packages/signals/src/global/signal.rs

@@ -1,171 +1,26 @@
-use crate::{read::Readable, ReadableRef};
-use crate::{write::Writable, GlobalKey};
-use crate::{WritableRef, Write};
-use dioxus_core::{prelude::ScopeId, Runtime};
-use generational_box::{BorrowResult, UnsyncStorage};
-use std::ops::Deref;
-
-use super::get_global_context;
+use super::{Global, InitializeFromFunction};
+use crate::read::Readable;
 use crate::read_impls;
 use crate::Signal;
 
-/// A signal that can be accessed from anywhere in the application and created in a static
-pub struct GlobalSignal<T> {
-    initializer: fn() -> T,
-    key: GlobalKey<'static>,
-    created_at: &'static std::panic::Location<'static>,
-}
-
-impl<T: 'static> GlobalSignal<T> {
-    /// Create a new global signal with the given initializer.
-    #[track_caller]
-    pub const fn new(initializer: fn() -> T) -> GlobalSignal<T> {
-        let key = std::panic::Location::caller();
-        GlobalSignal {
-            initializer,
-            key: GlobalKey::new(key),
-            created_at: key,
-        }
-    }
-
-    /// Get the key for this global
-    pub fn key(&self) -> GlobalKey<'static> {
-        self.key.clone()
-    }
-
-    /// Create this global signal with a specific key.
-    /// This is useful for ensuring that the signal is unique across the application and accessible from
-    /// outside the application too.
-    #[track_caller]
-    pub const fn with_key(initializer: fn() -> T, key: &'static str) -> GlobalSignal<T> {
-        GlobalSignal {
-            initializer,
-            key: GlobalKey::new_from_str(key),
-            created_at: std::panic::Location::caller(),
-        }
-    }
-
-    /// Get the signal that backs this .
-    pub fn signal(&self) -> Signal<T> {
-        let key = self.key();
-        let context = get_global_context();
-
-        let read = context.signal.borrow();
-
-        match read.get(&key) {
-            Some(signal) => *signal.downcast_ref::<Signal<T>>().unwrap(),
-            None => {
-                drop(read);
-
-                // Constructors are always run in the root scope
-                // The signal also exists in the root scope
-                let value = ScopeId::ROOT.in_runtime(self.initializer);
-                let signal = Signal::new_maybe_sync_in_scope_with_caller(
-                    value,
-                    ScopeId::ROOT,
-                    self.created_at,
-                );
-
-                let entry = context.signal.borrow_mut().insert(key, Box::new(signal));
-                debug_assert!(entry.is_none(), "Global signal already exists");
-
-                signal
-            }
-        }
-    }
-
-    #[doc(hidden)]
-    pub fn maybe_with_rt<O>(&self, f: impl FnOnce(&T) -> O) -> O {
-        if Runtime::current().is_err() {
-            f(&(self.initializer)())
-        } else {
-            self.with(f)
-        }
-    }
-
-    /// Write this value
-    pub fn write(&self) -> Write<'static, T, UnsyncStorage> {
-        self.signal().try_write_unchecked().unwrap()
-    }
-
-    /// Get the scope the signal was created in.
-    pub fn origin_scope(&self) -> ScopeId {
-        ScopeId::ROOT
+impl<T> InitializeFromFunction<T> for Signal<T> {
+    fn initialize_from_function(f: fn() -> T) -> Self {
+        Signal::new(f())
     }
+}
 
-    /// Run a closure with a mutable reference to the signal's value.
-    /// If the signal has been dropped, this will panic.
-    #[track_caller]
-    pub fn with_mut<O>(&self, f: impl FnOnce(&mut T) -> O) -> O {
-        self.signal().with_mut(f)
-    }
+/// A signal that can be accessed from anywhere in the application and created in a static
+pub type GlobalSignal<T> = Global<Signal<T>, T>;
 
+impl<T: 'static> GlobalSignal<T> {
     /// Get the generational id of the signal.
     pub fn id(&self) -> generational_box::GenerationalBoxId {
-        self.signal().id()
-    }
-}
-
-impl<T: 'static> Readable for GlobalSignal<T> {
-    type Target = T;
-    type Storage = UnsyncStorage;
-
-    #[track_caller]
-    fn try_read_unchecked(
-        &self,
-    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
-        self.signal().try_read_unchecked()
-    }
-
-    #[track_caller]
-    fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
-        self.signal().try_peek_unchecked()
+        self.resolve().id()
     }
-}
-
-impl<T: 'static> Writable for GlobalSignal<T> {
-    type Mut<'a, R: ?Sized + 'static> = Write<'a, R, UnsyncStorage>;
 
-    fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
-        ref_: Self::Mut<'_, I>,
-        f: F,
-    ) -> Self::Mut<'_, U> {
-        Write::map(ref_, f)
-    }
-
-    fn try_map_mut<
-        I: ?Sized + 'static,
-        U: ?Sized + 'static,
-        F: FnOnce(&mut I) -> Option<&mut U>,
-    >(
-        ref_: Self::Mut<'_, I>,
-        f: F,
-    ) -> Option<Self::Mut<'_, U>> {
-        Write::filter_map(ref_, f)
-    }
-
-    fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>(
-        mut_: Self::Mut<'a, R>,
-    ) -> Self::Mut<'b, R> {
-        Write::downcast_lifetime(mut_)
-    }
-
-    #[track_caller]
-    fn try_write_unchecked(
-        &self,
-    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
-        self.signal().try_write_unchecked()
-    }
-}
-
-/// Allow calling a signal with signal() syntax
-///
-/// Currently only limited to copy types, though could probably specialize for string/arc/rc
-impl<T: Clone + 'static> Deref for GlobalSignal<T> {
-    type Target = dyn Fn() -> T;
-
-    fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+    /// Resolve the global signal. This will try to get the existing value from the current virtual dom, and if it doesn't exist, it will create a new one.
+    pub fn signal(&self) -> Signal<T> {
+        self.resolve()
     }
 }
 

+ 1 - 1
packages/signals/src/map.rs

@@ -88,7 +88,7 @@ where
     type Target = dyn Fn() -> O;
 
     fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+        unsafe { Readable::deref_impl(self) }
     }
 }
 

+ 35 - 2
packages/signals/src/memo.rs

@@ -1,6 +1,6 @@
-use crate::read_impls;
 use crate::write::Writable;
 use crate::{read::Readable, ReadableRef, Signal};
+use crate::{read_impls, GlobalMemo};
 use crate::{CopyValue, ReadOnlySignal};
 use std::{
     cell::RefCell,
@@ -92,6 +92,39 @@ impl<T: 'static> Memo<T> {
         memo
     }
 
+    /// Creates a new [`GlobalMemo`] that can be used anywhere inside your dioxus app. This memo will automatically be created once per app the first time you use it.
+    ///
+    /// # Example
+    /// ```rust, no_run
+    /// # use dioxus::prelude::*;
+    /// static SIGNAL: GlobalSignal<i32> = Signal::global(|| 0);
+    /// // Create a new global memo that can be used anywhere in your app
+    /// static DOUBLED: GlobalMemo<i32> = Memo::global(|| SIGNAL() * 2);
+    ///
+    /// fn App() -> Element {
+    ///     rsx! {
+    ///         button {
+    ///             // When SIGNAL changes, the memo will update because the SIGNAL is read inside DOUBLED
+    ///             onclick: move |_| *SIGNAL.write() += 1,
+    ///             "{DOUBLED}"
+    ///         }
+    ///     }
+    /// }
+    /// ```
+    ///
+    /// <div class="warning">
+    ///
+    /// Global memos are generally not recommended for use in libraries because it makes it more difficult to allow multiple instances of components you define in your library.
+    ///
+    /// </div>
+    #[track_caller]
+    pub const fn global(constructor: fn() -> T) -> GlobalMemo<T>
+    where
+        T: PartialEq,
+    {
+        GlobalMemo::new(constructor)
+    }
+
     /// Rerun the computation and update the value of the memo if the result has changed.
     #[tracing::instrument(skip(self))]
     fn recompute(&self)
@@ -200,7 +233,7 @@ where
     type Target = dyn Fn() -> T;
 
     fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+        unsafe { Readable::deref_impl(self) }
     }
 }
 

+ 4 - 1
packages/signals/src/read.rs

@@ -221,8 +221,11 @@ pub trait Readable {
         <Self::Storage as AnyStorage>::map(self.read(), |v| v.index(index))
     }
 
+    /// SAFETY: You must call this function directly with `self` as the argument.
+    /// This function relies on the size of the object you return from the deref
+    /// being the same as the object you pass in
     #[doc(hidden)]
-    fn deref_impl<'a>(&self) -> &'a dyn Fn() -> Self::Target
+    unsafe fn deref_impl<'a>(&self) -> &'a dyn Fn() -> Self::Target
     where
         Self: Sized + 'a,
         Self::Target: Clone,

+ 1 - 1
packages/signals/src/read_only_signal.rs

@@ -132,7 +132,7 @@ impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for ReadOnlySignal<T,
     type Target = dyn Fn() -> T;
 
     fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+        unsafe { Readable::deref_impl(self) }
     }
 }
 

+ 7 - 4
packages/signals/src/signal.rs

@@ -1,4 +1,4 @@
-use crate::{default_impl, fmt_impls, write_impls};
+use crate::{default_impl, fmt_impls, write_impls, Global};
 use crate::{read::*, write::*, CopyValue, GlobalMemo, GlobalSignal, ReadableRef};
 use crate::{Memo, WritableRef};
 use dioxus_core::prelude::*;
@@ -87,7 +87,7 @@ impl<T: 'static> Signal<T> {
     /// </div>
     #[track_caller]
     pub const fn global(constructor: fn() -> T) -> GlobalSignal<T> {
-        GlobalSignal::new(constructor)
+        Global::new(constructor)
     }
 }
 
@@ -118,7 +118,10 @@ impl<T: PartialEq + 'static> Signal<T> {
     ///
     /// </div>
     #[track_caller]
-    pub const fn global_memo(constructor: fn() -> T) -> GlobalMemo<T> {
+    pub const fn global_memo(constructor: fn() -> T) -> GlobalMemo<T>
+    where
+        T: PartialEq,
+    {
         GlobalMemo::new(constructor)
     }
 
@@ -462,7 +465,7 @@ impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for Signal<T, S> {
     type Target = dyn Fn() -> T;
 
     fn deref(&self) -> &Self::Target {
-        Readable::deref_impl(self)
+        unsafe { Readable::deref_impl(self) }
     }
 }