فهرست منبع

Merge pull request #2022 from ealmloff/signal-lifetimes

Restore signal lifetimes; rename take to manually drop
Jonathan Kelley 1 سال پیش
والد
کامیت
4a27b16cc5

+ 1 - 1
examples/dog_app.rs

@@ -80,7 +80,7 @@ fn BreedPic(breed: Signal<String>) -> Element {
             .await
     });
 
-    match fut.read().as_ref() {
+    match fut.read_unchecked().as_ref() {
         Some(Ok(resp)) => rsx! {
             button { onclick: move |_| fut.restart(), "Click to fetch another doggo" }
             img { max_width: "500px", max_height: "500px", src: "{resp.message}" }

+ 1 - 1
examples/suspense.rs

@@ -62,7 +62,7 @@ fn Doggo() -> Element {
             .await
     });
 
-    match fut.read().as_ref() {
+    match fut.read_unchecked().as_ref() {
         Some(Ok(resp)) => rsx! {
             button { onclick: move |_| fut.restart(), "Click to fetch another doggo" }
             div { img { max_width: "500px", max_height: "500px", src: "{resp.message}" } }

+ 1 - 2
packages/desktop/headless_tests/rendering.rs

@@ -25,14 +25,13 @@ fn use_inner_html(id: &'static str) -> Option<String> {
             .unwrap();
 
             if let Some(html) = res.as_str() {
-                // serde_json::Value::String(html)
                 println!("html: {}", html);
                 value.set(Some(html.to_string()));
             }
         });
     });
 
-    value.read().clone()
+    value()
 }
 
 const EXPECTED_HTML: &str = r#"<div style="width: 100px; height: 100px; color: rgb(0, 0, 0);" id="5"><input type="checkbox"><h1>text</h1><div><p>hello world</p></div></div>"#;

+ 34 - 20
packages/generational-box/src/lib.rs

@@ -98,7 +98,7 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
 
     /// Try to read the value. Returns None if the value is no longer valid.
     #[track_caller]
-    pub fn try_read(&self) -> Result<S::Ref<T>, BorrowError> {
+    pub fn try_read(&self) -> Result<S::Ref<'static, T>, BorrowError> {
         if !self.validate() {
             return Err(BorrowError::Dropped(ValueDroppedError {
                 #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -129,13 +129,13 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
 
     /// Read the value. Panics if the value is no longer valid.
     #[track_caller]
-    pub fn read(&self) -> S::Ref<T> {
+    pub fn read(&self) -> S::Ref<'static, T> {
         self.try_read().unwrap()
     }
 
     /// Try to write the value. Returns None if the value is no longer valid.
     #[track_caller]
-    pub fn try_write(&self) -> Result<S::Mut<T>, BorrowMutError> {
+    pub fn try_write(&self) -> Result<S::Mut<'static, T>, BorrowMutError> {
         if !self.validate() {
             return Err(BorrowMutError::Dropped(ValueDroppedError {
                 #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -162,7 +162,7 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
 
     /// Write the value. Panics if the value is no longer valid.
     #[track_caller]
-    pub fn write(&self) -> S::Mut<T> {
+    pub fn write(&self) -> S::Mut<'static, T> {
         self.try_write().unwrap()
     }
 
@@ -186,8 +186,8 @@ impl<T: 'static, S: Storage<T>> GenerationalBox<T, S> {
         }
     }
 
-    /// Take the value out of the generational box and invalidate the generational box. This will return the value if the value was taken.
-    pub fn take(&self) -> Option<T> {
+    /// Drop the value out of the generational box and invalidate the generational box. This will return the value if the value was taken.
+    pub fn manually_drop(&self) -> Option<T> {
         if self.validate() {
             Storage::take(&self.raw.0.data)
         } else {
@@ -210,13 +210,13 @@ pub trait Storage<Data = ()>: AnyStorage + 'static {
     fn try_read(
         &'static self,
         #[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefBorrowInfo,
-    ) -> Result<Self::Ref<Data>, BorrowError>;
+    ) -> Result<Self::Ref<'static, Data>, BorrowError>;
 
     /// Try to write the value. Returns None if the value is no longer valid.
     fn try_write(
         &'static self,
         #[cfg(any(debug_assertions, feature = "debug_ownership"))] at: GenerationalRefMutBorrowInfo,
-    ) -> Result<Self::Mut<Data>, BorrowMutError>;
+    ) -> Result<Self::Mut<'static, Data>, BorrowMutError>;
 
     /// Set the value
     fn set(&'static self, value: Data);
@@ -228,35 +228,49 @@ pub trait Storage<Data = ()>: AnyStorage + 'static {
 /// A trait for any storage backing type.
 pub trait AnyStorage: Default {
     /// The reference this storage type returns.
-    type Ref<T: ?Sized + 'static>: Deref<Target = T> + 'static;
+    type Ref<'a, T: ?Sized + 'static>: Deref<Target = T>;
     /// The mutable reference this storage type returns.
-    type Mut<T: ?Sized + 'static>: DerefMut<Target = T> + 'static;
+    type Mut<'a, T: ?Sized + 'static>: DerefMut<Target = T>;
+
+    /// Downcast a reference in a Ref to a more specific lifetime
+    ///
+    /// This function enforces the variance of the lifetime parameter `'a` in Ref. Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
+    fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
+        ref_: Self::Ref<'a, T>,
+    ) -> Self::Ref<'b, T>;
+
+    /// Downcast a mutable reference in a RefMut to a more specific lifetime
+    ///
+    /// This function enforces the variance of the lifetime parameter `'a` in Mut.  Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
+    fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
+        mut_: Self::Mut<'a, T>,
+    ) -> Self::Mut<'b, T>;
 
     /// Try to map the mutable ref.
-    fn try_map_mut<T: ?Sized, U: ?Sized + 'static>(
-        mut_ref: Self::Mut<T>,
+    fn try_map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
+        mut_ref: Self::Mut<'_, T>,
         f: impl FnOnce(&mut T) -> Option<&mut U>,
-    ) -> Option<Self::Mut<U>>;
+    ) -> Option<Self::Mut<'_, U>>;
 
     /// Map the mutable ref.
-    fn map_mut<T: ?Sized, U: ?Sized + 'static>(
-        mut_ref: Self::Mut<T>,
+    fn map_mut<T: ?Sized + 'static, U: ?Sized + 'static>(
+        mut_ref: Self::Mut<'_, T>,
         f: impl FnOnce(&mut T) -> &mut U,
-    ) -> Self::Mut<U> {
+    ) -> Self::Mut<'_, U> {
         Self::try_map_mut(mut_ref, |v| Some(f(v))).unwrap()
     }
 
     /// Try to map the ref.
     fn try_map<T: ?Sized, U: ?Sized + 'static>(
-        ref_: Self::Ref<T>,
+        ref_: Self::Ref<'_, T>,
         f: impl FnOnce(&T) -> Option<&U>,
-    ) -> Option<Self::Ref<U>>;
+    ) -> Option<Self::Ref<'_, U>>;
 
     /// Map the ref.
     fn map<T: ?Sized, U: ?Sized + 'static>(
-        ref_: Self::Ref<T>,
+        ref_: Self::Ref<'_, T>,
         f: impl FnOnce(&T) -> &U,
-    ) -> Self::Ref<U> {
+    ) -> Self::Ref<'_, U> {
         Self::try_map(ref_, |v| Some(f(v))).unwrap()
     }
 

+ 22 - 10
packages/generational-box/src/sync.rs

@@ -20,13 +20,25 @@ fn sync_runtime() -> &'static Arc<Mutex<Vec<MemoryLocation<SyncStorage>>>> {
 }
 
 impl AnyStorage for SyncStorage {
-    type Ref<R: ?Sized + 'static> = GenerationalRef<MappedRwLockReadGuard<'static, R>>;
-    type Mut<W: ?Sized + 'static> = GenerationalRefMut<MappedRwLockWriteGuard<'static, W>>;
+    type Ref<'a, R: ?Sized + 'static> = GenerationalRef<MappedRwLockReadGuard<'a, R>>;
+    type Mut<'a, W: ?Sized + 'static> = GenerationalRefMut<MappedRwLockWriteGuard<'a, W>>;
 
-    fn try_map<I: ?Sized, U: ?Sized + 'static>(
-        ref_: Self::Ref<I>,
+    fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
+        ref_: Self::Ref<'a, T>,
+    ) -> Self::Ref<'b, T> {
+        ref_
+    }
+
+    fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
+        mut_: Self::Mut<'a, T>,
+    ) -> Self::Mut<'b, T> {
+        mut_
+    }
+
+    fn try_map<I: ?Sized + 'static, U: ?Sized + 'static>(
+        ref_: Self::Ref<'_, I>,
         f: impl FnOnce(&I) -> Option<&U>,
-    ) -> Option<Self::Ref<U>> {
+    ) -> Option<Self::Ref<'_, U>> {
         let GenerationalRef {
             inner,
             #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -46,10 +58,10 @@ impl AnyStorage for SyncStorage {
             })
     }
 
-    fn try_map_mut<I: ?Sized, U: ?Sized + 'static>(
-        mut_ref: Self::Mut<I>,
+    fn try_map_mut<I: ?Sized + 'static, U: ?Sized + 'static>(
+        mut_ref: Self::Mut<'_, I>,
         f: impl FnOnce(&mut I) -> Option<&mut U>,
-    ) -> Option<Self::Mut<U>> {
+    ) -> Option<Self::Mut<'_, U>> {
         let GenerationalRefMut {
             inner,
             #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -101,7 +113,7 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
         &'static self,
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
         at: crate::GenerationalRefBorrowInfo,
-    ) -> Result<Self::Ref<T>, error::BorrowError> {
+    ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
         let read = self.0.try_read();
 
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
@@ -132,7 +144,7 @@ impl<T: Sync + Send + 'static> Storage<T> for SyncStorage {
         &'static self,
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
         at: crate::GenerationalRefMutBorrowInfo,
-    ) -> Result<Self::Mut<T>, error::BorrowMutError> {
+    ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
         let write = self.0.try_write();
 
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]

+ 22 - 10
packages/generational-box/src/unsync.rs

@@ -15,7 +15,7 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
 
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
         at: crate::GenerationalRefBorrowInfo,
-    ) -> Result<Self::Ref<T>, error::BorrowError> {
+    ) -> Result<Self::Ref<'static, T>, error::BorrowError> {
         let borrow = self.0.try_borrow();
 
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
@@ -46,7 +46,7 @@ impl<T: 'static> Storage<T> for UnsyncStorage {
         &'static self,
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
         at: crate::GenerationalRefMutBorrowInfo,
-    ) -> Result<Self::Mut<T>, error::BorrowMutError> {
+    ) -> Result<Self::Mut<'static, T>, error::BorrowMutError> {
         let borrow = self.0.try_borrow_mut();
 
         #[cfg(any(debug_assertions, feature = "debug_ownership"))]
@@ -89,13 +89,25 @@ thread_local! {
 }
 
 impl AnyStorage for UnsyncStorage {
-    type Ref<R: ?Sized + 'static> = GenerationalRef<Ref<'static, R>>;
-    type Mut<W: ?Sized + 'static> = GenerationalRefMut<RefMut<'static, W>>;
+    type Ref<'a, R: ?Sized + 'static> = GenerationalRef<Ref<'a, R>>;
+    type Mut<'a, W: ?Sized + 'static> = GenerationalRefMut<RefMut<'a, W>>;
 
-    fn try_map<I: ?Sized, U: ?Sized + 'static>(
-        _self: Self::Ref<I>,
+    fn downcast_lifetime_ref<'a: 'b, 'b, T: ?Sized + 'static>(
+        ref_: Self::Ref<'a, T>,
+    ) -> Self::Ref<'b, T> {
+        ref_
+    }
+
+    fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
+        mut_: Self::Mut<'a, T>,
+    ) -> Self::Mut<'b, T> {
+        mut_
+    }
+
+    fn try_map<I: ?Sized + 'static, U: ?Sized + 'static>(
+        _self: Self::Ref<'_, I>,
         f: impl FnOnce(&I) -> Option<&U>,
-    ) -> Option<Self::Ref<U>> {
+    ) -> Option<Self::Ref<'_, U>> {
         let GenerationalRef {
             inner,
             #[cfg(any(debug_assertions, feature = "debug_borrows"))]
@@ -109,10 +121,10 @@ impl AnyStorage for UnsyncStorage {
         })
     }
 
-    fn try_map_mut<I: ?Sized, U: ?Sized + 'static>(
-        mut_ref: Self::Mut<I>,
+    fn try_map_mut<I: ?Sized + 'static, U: ?Sized + 'static>(
+        mut_ref: Self::Mut<'_, I>,
         f: impl FnOnce(&mut I) -> Option<&mut U>,
-    ) -> Option<Self::Mut<U>> {
+    ) -> Option<Self::Mut<'_, U>> {
         let GenerationalRefMut {
             inner,
             #[cfg(any(debug_assertions, feature = "debug_borrows"))]

+ 1 - 1
packages/router/examples/simple_routes.rs

@@ -123,7 +123,7 @@ fn Route3(dynamic: String) -> Element {
             oninput: move |evt| {
                 *current_route_str.write() = evt.value();
             },
-            value: "{current_route_str.read()}"
+            value: "{current_route_str}"
         }
         "dynamic: {dynamic}"
         Link { to: Route::Route2 { user_id: 8888 }, "hello world link" }

+ 8 - 8
packages/router/src/contexts/router.rs

@@ -157,7 +157,7 @@ impl RouterContext {
     /// Will fail silently if there is no previous location to go to.
     pub fn go_back(&self) {
         {
-            self.inner.clone().write().history.go_back();
+            self.inner.write_unchecked().history.go_back();
         }
 
         self.change_route();
@@ -168,7 +168,7 @@ impl RouterContext {
     /// Will fail silently if there is no next location to go to.
     pub fn go_forward(&self) {
         {
-            self.inner.clone().write().history.go_forward();
+            self.inner.write_unchecked().history.go_forward();
         }
 
         self.change_route();
@@ -179,7 +179,7 @@ impl RouterContext {
         target: NavigationTarget<Rc<dyn Any>>,
     ) -> Option<ExternalNavigationFailure> {
         {
-            let mut write = self.inner.clone().write();
+            let mut write = self.inner.write_unchecked();
             match target {
                 NavigationTarget::Internal(p) => write.history.push(p),
                 NavigationTarget::External(e) => return write.external(e),
@@ -195,7 +195,7 @@ impl RouterContext {
     pub fn push(&self, target: impl Into<IntoRoutable>) -> Option<ExternalNavigationFailure> {
         let target = self.resolve_into_routable(target.into());
         {
-            let mut write = self.inner.clone().write();
+            let mut write = self.inner.write_unchecked();
             match target {
                 NavigationTarget::Internal(p) => write.history.push(p),
                 NavigationTarget::External(e) => return write.external(e),
@@ -212,7 +212,7 @@ impl RouterContext {
         let target = self.resolve_into_routable(target.into());
 
         {
-            let mut state = self.inner.clone().write();
+            let mut state = self.inner.write_unchecked();
             match target {
                 NavigationTarget::Internal(p) => state.history.replace(p),
                 NavigationTarget::External(e) => return state.external(e),
@@ -276,14 +276,14 @@ impl RouterContext {
 
     /// Clear any unresolved errors
     pub fn clear_error(&self) {
-        let mut write_inner = self.inner.clone().write();
+        let mut write_inner = self.inner.write_unchecked();
         write_inner.unresolved_error = None;
 
         write_inner.update_subscribers();
     }
 
     pub(crate) fn render_error(&self) -> Element {
-        let inner_read = self.inner.clone().write();
+        let inner_read = self.inner.write_unchecked();
         inner_read
             .unresolved_error
             .as_ref()
@@ -297,7 +297,7 @@ impl RouterContext {
             let callback = callback.clone();
             drop(self_read);
             if let Some(new) = callback(myself) {
-                let mut self_write = self.inner.clone().write();
+                let mut self_write = self.inner.write_unchecked();
                 match new {
                     NavigationTarget::Internal(p) => self_write.history.replace(p),
                     NavigationTarget::External(e) => return self_write.external(e),

+ 4 - 4
packages/signals/examples/errors.rs

@@ -30,9 +30,9 @@ fn app() -> Element {
 
 #[component]
 fn Read() -> Element {
-    let mut signal = use_signal_sync(|| 0);
+    let signal = use_signal_sync(|| 0);
 
-    let _write = signal.write();
+    let _write = signal.write_unchecked();
     let _read = signal.read();
 
     unreachable!()
@@ -40,10 +40,10 @@ fn Read() -> Element {
 
 #[component]
 fn ReadMut() -> Element {
-    let mut signal = use_signal_sync(|| 0);
+    let signal = use_signal_sync(|| 0);
 
     let _read = signal.read();
-    let _write = signal.write();
+    let _write = signal.write_unchecked();
 
     unreachable!()
 }

+ 21 - 17
packages/signals/src/copy_value.rs

@@ -15,6 +15,7 @@ use generational_box::{GenerationalBox, Owner, Storage};
 
 use crate::ReadableRef;
 use crate::Writable;
+use crate::WritableRef;
 use crate::{ReactiveContext, Readable};
 
 /// Run a closure with the given owner.
@@ -189,11 +190,9 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
         }
     }
 
-    /// Take the value out of the CopyValue, invalidating the value in the process.
-    pub fn take(&self) -> T {
-        self.value
-            .take()
-            .expect("value is already dropped or borrowed")
+    /// Manually drop the value in the CopyValue, invalidating the value in the process.
+    pub fn manually_drop(&self) -> Option<T> {
+        self.value.manually_drop()
     }
 
     /// Get the scope this value was created in.
@@ -211,40 +210,45 @@ impl<T: 'static, S: Storage<T>> Readable for CopyValue<T, S> {
     type Target = T;
     type Storage = S;
 
-    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
         self.value.try_read()
     }
 
-    fn peek(&self) -> ReadableRef<Self> {
+    fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
         self.value.read()
     }
 }
 
 impl<T: 'static, S: Storage<T>> Writable for CopyValue<T, S> {
-    type Mut<R: ?Sized + 'static> = S::Mut<R>;
+    type Mut<'a, R: ?Sized + 'static> = S::Mut<'a, R>;
 
     fn map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> &mut U>(
-        mut_: Self::Mut<I>,
+        mut_: Self::Mut<'_, I>,
         f: F,
-    ) -> Self::Mut<U> {
+    ) -> Self::Mut<'_, U> {
         S::map_mut(mut_, f)
     }
 
     fn try_map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> Option<&mut U>>(
-        mut_: Self::Mut<I>,
+        mut_: Self::Mut<'_, I>,
         f: F,
-    ) -> Option<Self::Mut<U>> {
+    ) -> Option<Self::Mut<'_, U>> {
         S::try_map_mut(mut_, f)
     }
 
-    #[track_caller]
-    fn try_write(&mut self) -> Result<Self::Mut<T>, generational_box::BorrowMutError> {
-        self.value.try_write()
+    fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>(
+        mut_: Self::Mut<'a, R>,
+    ) -> Self::Mut<'b, R> {
+        S::downcast_lifetime_mut(mut_)
     }
 
     #[track_caller]
-    fn write(&mut self) -> Self::Mut<T> {
-        self.value.write()
+    fn try_write_unchecked(
+        &self,
+    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
+        self.value.try_write()
     }
 
     #[track_caller]

+ 6 - 4
packages/signals/src/global/memo.rs

@@ -56,13 +56,15 @@ impl<T: PartialEq + 'static> Readable for GlobalMemo<T> {
     type Storage = UnsyncStorage;
 
     #[track_caller]
-    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
-        self.memo().try_read()
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
+        self.memo().try_read_unchecked()
     }
 
     #[track_caller]
-    fn peek(&self) -> ReadableRef<Self> {
-        self.memo().peek()
+    fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
+        self.memo().peek_unchecked()
     }
 }
 

+ 24 - 14
packages/signals/src/global/signal.rs

@@ -1,6 +1,6 @@
 use crate::write::Writable;
-use crate::Write;
 use crate::{read::Readable, ReadableRef};
+use crate::{WritableRef, Write};
 use dioxus_core::prelude::{IntoAttributeValue, ScopeId};
 use generational_box::UnsyncStorage;
 use std::{mem::MaybeUninit, ops::Deref};
@@ -44,8 +44,8 @@ impl<T: 'static> GlobalSignal<T> {
     }
 
     /// Write this value
-    pub fn write(&self) -> Write<T, UnsyncStorage> {
-        self.signal().write()
+    pub fn write(&self) -> Write<'static, T, UnsyncStorage> {
+        self.signal().try_write_unchecked().unwrap()
     }
 
     /// Get the scope the signal was created in.
@@ -71,23 +71,25 @@ impl<T: 'static> Readable for GlobalSignal<T> {
     type Storage = UnsyncStorage;
 
     #[track_caller]
-    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
-        self.signal().try_read()
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
+        self.signal().try_read_unchecked()
     }
 
     #[track_caller]
-    fn peek(&self) -> ReadableRef<Self> {
-        self.signal().peek()
+    fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
+        self.signal().peek_unchecked()
     }
 }
 
 impl<T: 'static> Writable for GlobalSignal<T> {
-    type Mut<R: ?Sized + 'static> = Write<R, UnsyncStorage>;
+    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>,
+        ref_: Self::Mut<'_, I>,
         f: F,
-    ) -> Self::Mut<U> {
+    ) -> Self::Mut<'_, U> {
         Write::map(ref_, f)
     }
 
@@ -96,15 +98,23 @@ impl<T: 'static> Writable for GlobalSignal<T> {
         U: ?Sized + 'static,
         F: FnOnce(&mut I) -> Option<&mut U>,
     >(
-        ref_: Self::Mut<I>,
+        ref_: Self::Mut<'_, I>,
         f: F,
-    ) -> Option<Self::Mut<U>> {
+    ) -> 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(&mut self) -> Result<Self::Mut<T>, generational_box::BorrowMutError> {
-        self.signal().try_write()
+    fn try_write_unchecked(
+        &self,
+    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
+        self.signal().try_write_unchecked()
     }
 }
 

+ 10 - 6
packages/signals/src/map.rs

@@ -6,8 +6,8 @@ use generational_box::{AnyStorage, UnsyncStorage};
 
 /// A read only signal that has been mapped to a new type.
 pub struct MappedSignal<O: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
-    try_read: Rc<dyn Fn() -> Result<S::Ref<O>, generational_box::BorrowError> + 'static>,
-    peek: Rc<dyn Fn() -> S::Ref<O> + 'static>,
+    try_read: Rc<dyn Fn() -> Result<S::Ref<'static, O>, generational_box::BorrowError> + 'static>,
+    peek: Rc<dyn Fn() -> S::Ref<'static, O> + 'static>,
 }
 
 impl<O: ?Sized, S: AnyStorage> Clone for MappedSignal<O, S> {
@@ -26,8 +26,10 @@ where
 {
     /// Create a new mapped signal.
     pub(crate) fn new(
-        try_read: Rc<dyn Fn() -> Result<S::Ref<O>, generational_box::BorrowError> + 'static>,
-        peek: Rc<dyn Fn() -> S::Ref<O> + 'static>,
+        try_read: Rc<
+            dyn Fn() -> Result<S::Ref<'static, O>, generational_box::BorrowError> + 'static,
+        >,
+        peek: Rc<dyn Fn() -> S::Ref<'static, O> + 'static>,
     ) -> Self {
         MappedSignal { try_read, peek }
     }
@@ -41,11 +43,13 @@ where
     type Target = O;
     type Storage = S;
 
-    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
         (self.try_read)()
     }
 
-    fn peek(&self) -> ReadableRef<Self> {
+    fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
         (self.peek)()
     }
 }

+ 7 - 5
packages/signals/src/memo.rs

@@ -163,8 +163,10 @@ where
     type Storage = UnsyncStorage;
 
     #[track_caller]
-    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
-        let read = self.inner.try_read();
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
+        let read = self.inner.try_read_unchecked();
         match read {
             Ok(r) => {
                 let needs_update = self
@@ -175,7 +177,7 @@ where
                 if needs_update {
                     drop(r);
                     self.recompute();
-                    self.inner.try_read()
+                    self.inner.try_read_unchecked()
                 } else {
                     Ok(r)
                 }
@@ -188,8 +190,8 @@ where
     ///
     /// If the signal has been dropped, this will panic.
     #[track_caller]
-    fn peek(&self) -> ReadableRef<Self> {
-        self.inner.peek()
+    fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
+        self.inner.peek_unchecked()
     }
 }
 

+ 1 - 2
packages/signals/src/reactive_context.rs

@@ -128,8 +128,7 @@ impl ReactiveContext {
     ///
     /// Returns true if the context was marked as dirty, or false if the context has been dropped
     pub fn mark_dirty(&self) -> bool {
-        let mut copy = self.inner;
-        if let Ok(mut self_write) = copy.try_write() {
+        if let Ok(mut self_write) = self.inner.try_write_unchecked() {
             #[cfg(debug_assertions)]
             {
                 tracing::trace!(

+ 39 - 9
packages/signals/src/read.rs

@@ -6,7 +6,8 @@ use crate::MappedSignal;
 
 /// A reference to a value that can be read from.
 #[allow(type_alias_bounds)]
-pub type ReadableRef<T: Readable, O = <T as Readable>::Target> = <T::Storage as AnyStorage>::Ref<O>;
+pub type ReadableRef<'a, T: Readable, O = <T as Readable>::Target> =
+    <T::Storage as AnyStorage>::Ref<'a, O>;
 
 /// A trait for states that can be read from like [`crate::Signal`], [`crate::GlobalSignal`], or [`crate::ReadOnlySignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Readable`] type.
 pub trait Readable {
@@ -27,15 +28,17 @@ pub trait Readable {
             let mapping = mapping.clone();
             move || {
                 self_
-                    .try_read()
+                    .try_read_unchecked()
                     .map(|ref_| <Self::Storage as AnyStorage>::map(ref_, |r| mapping(r)))
             }
         })
             as Rc<
-                dyn Fn() -> Result<ReadableRef<Self, O>, generational_box::BorrowError> + 'static,
+                dyn Fn() -> Result<ReadableRef<'static, Self, O>, generational_box::BorrowError>
+                    + 'static,
             >;
-        let peek = Rc::new(move || <Self::Storage as AnyStorage>::map(self.peek(), |r| mapping(r)))
-            as Rc<dyn Fn() -> ReadableRef<Self, O> + 'static>;
+        let peek = Rc::new(move || {
+            <Self::Storage as AnyStorage>::map(self.peek_unchecked(), |r| mapping(r))
+        }) as Rc<dyn Fn() -> ReadableRef<'static, Self, O> + 'static>;
         MappedSignal::new(try_read, peek)
     }
 
@@ -47,12 +50,39 @@ pub trait Readable {
         self.try_read().unwrap()
     }
 
-    /// Try to get the current value of the state. If this is a signal, this will subscribe the current scope to the signal. If the value has been dropped, this will panic.
+    /// Try to get the current value of the state. If this is a signal, this will subscribe the current scope to the signal.
     #[track_caller]
-    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError>;
+    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
+        self.try_read_unchecked()
+            .map(Self::Storage::downcast_lifetime_ref)
+    }
+
+    /// Try to get a reference to the value without checking the lifetime.
+    ///
+    /// NOTE: This method is completely safe because borrow checking is done at runtime.
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>;
+
+    /// Tet a reference to the value without checking the lifetime.
+    ///
+    /// NOTE: This method is completely safe because borrow checking is done at runtime.
+    fn read_unchecked(&self) -> ReadableRef<'static, Self> {
+        self.try_read_unchecked().unwrap()
+    }
+
+    /// Get the current value of the signal without checking the lifetime. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
+    ///
+    /// If the signal has been dropped, this will panic.
+    ///
+    /// NOTE: This method is completely safe because borrow checking is done at runtime.
+    fn peek_unchecked(&self) -> ReadableRef<'static, Self>;
 
     /// Get the current value of the state without subscribing to updates. If the value has been dropped, this will panic.
-    fn peek(&self) -> ReadableRef<Self>;
+    #[track_caller]
+    fn peek(&self) -> ReadableRef<Self> {
+        Self::Storage::downcast_lifetime_ref(self.peek_unchecked())
+    }
 
     /// Clone the inner value and return it. If the value has been dropped, this will panic.
     #[track_caller]
@@ -171,7 +201,7 @@ pub struct ReadableValueIterator<'a, R> {
 }
 
 impl<'a, T: 'static, R: Readable<Target = Vec<T>>> Iterator for ReadableValueIterator<'a, R> {
-    type Item = ReadableRef<R, T>;
+    type Item = ReadableRef<'a, R, T>;
 
     fn next(&mut self) -> Option<Self::Item> {
         let index = self.index;

+ 9 - 5
packages/signals/src/read_only_signal.rs

@@ -50,7 +50,9 @@ impl<T: 'static, S: Storage<SignalData<T>>> ReadOnlySignal<T, S> {
     #[doc(hidden)]
     /// This should only be used by the `rsx!` macro.
     pub fn __take(&self) -> T {
-        self.inner.take()
+        self.inner
+            .manually_drop()
+            .expect("Signal has already been dropped")
     }
 }
 
@@ -59,16 +61,18 @@ impl<T, S: Storage<SignalData<T>>> Readable for ReadOnlySignal<T, S> {
     type Storage = S;
 
     #[track_caller]
-    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
-        self.inner.try_read()
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
+        self.inner.try_read_unchecked()
     }
 
     /// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
     ///
     /// If the signal has been dropped, this will panic.
     #[track_caller]
-    fn peek(&self) -> S::Ref<T> {
-        self.inner.peek()
+    fn peek_unchecked(&self) -> S::Ref<'static, T> {
+        self.inner.peek_unchecked()
     }
 }
 

+ 46 - 22
packages/signals/src/signal.rs

@@ -1,8 +1,8 @@
-use crate::Memo;
 use crate::{
     read::Readable, write::Writable, CopyValue, GlobalMemo, GlobalSignal, ReactiveContext,
     ReadableRef,
 };
+use crate::{Memo, WritableRef};
 use dioxus_core::{prelude::IntoAttributeValue, ScopeId};
 use generational_box::{AnyStorage, Storage, SyncStorage, UnsyncStorage};
 use std::{
@@ -136,9 +136,9 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
         }
     }
 
-    /// Take the value out of the signal, invalidating the signal in the process.
-    pub fn take(&self) -> T {
-        self.inner.take().value
+    /// Drop the value out of the signal, invalidating the signal in the process.
+    pub fn manually_drop(&self) -> Option<T> {
+        self.inner.manually_drop().map(|i| i.value)
     }
 
     /// Get the scope the signal was created in.
@@ -168,8 +168,10 @@ impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
     type Storage = S;
 
     #[track_caller]
-    fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
-        let inner = self.inner.try_read()?;
+    fn try_read_unchecked(
+        &self,
+    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
+        let inner = self.inner.try_read_unchecked()?;
 
         if let Some(reactive_context) = ReactiveContext::current() {
             tracing::trace!("Subscribing to the reactive context {}", reactive_context);
@@ -182,19 +184,20 @@ impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
     /// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
     ///
     /// If the signal has been dropped, this will panic.
-    fn peek(&self) -> ReadableRef<Self> {
-        let inner = self.inner.read();
+    #[track_caller]
+    fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
+        let inner = self.inner.try_read_unchecked().unwrap();
         S::map(inner, |v| &v.value)
     }
 }
 
 impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
-    type Mut<R: ?Sized + 'static> = Write<R, S>;
+    type Mut<'a, R: ?Sized + 'static> = Write<'a, R, S>;
 
     fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
-        ref_: Self::Mut<I>,
+        ref_: Self::Mut<'_, I>,
         f: F,
-    ) -> Self::Mut<U> {
+    ) -> Self::Mut<'_, U> {
         Write::map(ref_, f)
     }
 
@@ -203,15 +206,23 @@ impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
         U: ?Sized + 'static,
         F: FnOnce(&mut I) -> Option<&mut U>,
     >(
-        ref_: Self::Mut<I>,
+        ref_: Self::Mut<'_, I>,
         f: F,
-    ) -> Option<Self::Mut<U>> {
+    ) -> 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(&mut self) -> Result<Self::Mut<T>, generational_box::BorrowMutError> {
-        self.inner.try_write().map(|inner| {
+    fn try_write_unchecked(
+        &self,
+    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
+        self.inner.try_write_unchecked().map(|inner| {
             let borrow = S::map_mut(inner, |v| &mut v.value);
             Write {
                 write: borrow,
@@ -273,14 +284,14 @@ impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
 ///
 /// T is the current type of the write
 /// S is the storage type of the signal
-pub struct Write<T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
-    write: S::Mut<T>,
+pub struct Write<'a, T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
+    write: S::Mut<'a, T>,
     drop_signal: Box<dyn Any>,
 }
 
-impl<T: ?Sized + 'static, S: AnyStorage> Write<T, S> {
+impl<'a, T: ?Sized + 'static, S: AnyStorage> Write<'a, T, S> {
     /// Map the mutable reference to the signal's value to a new type.
-    pub fn map<O: ?Sized>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<O, S> {
+    pub fn map<O: ?Sized>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, S> {
         let Self {
             write, drop_signal, ..
         } = myself;
@@ -294,16 +305,29 @@ impl<T: ?Sized + 'static, S: AnyStorage> Write<T, S> {
     pub fn filter_map<O: ?Sized>(
         myself: Self,
         f: impl FnOnce(&mut T) -> Option<&mut O>,
-    ) -> Option<Write<O, S>> {
+    ) -> Option<Write<'a, O, S>> {
         let Self {
             write, drop_signal, ..
         } = myself;
         let write = S::try_map_mut(write, f);
         write.map(|write| Write { write, drop_signal })
     }
+
+    /// Downcast the lifetime of the mutable reference to the signal's value.
+    ///
+    /// This function enforces the variance of the lifetime parameter `'a` in Mut.  Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
+    pub fn downcast_lifetime<'b>(mut_: Self) -> Write<'b, T, S>
+    where
+        'a: 'b,
+    {
+        Write {
+            write: S::downcast_lifetime_mut(mut_.write),
+            drop_signal: mut_.drop_signal,
+        }
+    }
 }
 
-impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<T, S> {
+impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<'_, T, S> {
     type Target = T;
 
     fn deref(&self) -> &Self::Target {
@@ -311,7 +335,7 @@ impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<T, S> {
     }
 }
 
-impl<T: ?Sized, S: AnyStorage> DerefMut for Write<T, S> {
+impl<T: ?Sized, S: AnyStorage> DerefMut for Write<'_, T, S> {
     fn deref_mut(&mut self) -> &mut Self::Target {
         &mut self.write
     }

+ 58 - 25
packages/signals/src/write.rs

@@ -1,33 +1,60 @@
-use std::ops::DerefMut;
-use std::ops::IndexMut;
+use std::ops::{DerefMut, IndexMut};
 
 use crate::read::Readable;
 
+/// A reference to a value that can be read from.
+#[allow(type_alias_bounds)]
+pub type WritableRef<'a, T: Writable, O = <T as Readable>::Target> = T::Mut<'a, O>;
+
 /// A trait for states that can be read from like [`crate::Signal`], or [`crate::GlobalSignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Writable`] type.
 pub trait Writable: Readable {
     /// The type of the reference.
-    type Mut<R: ?Sized + 'static>: DerefMut<Target = R> + 'static;
+    type Mut<'a, R: ?Sized + 'static>: DerefMut<Target = R>;
 
     /// Map the reference to a new type.
     fn map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> &mut U>(
-        ref_: Self::Mut<I>,
+        ref_: Self::Mut<'_, I>,
         f: F,
-    ) -> Self::Mut<U>;
+    ) -> Self::Mut<'_, U>;
 
     /// Try to map the reference to a new type.
     fn try_map_mut<I: ?Sized, U: ?Sized, F: FnOnce(&mut I) -> Option<&mut U>>(
-        ref_: Self::Mut<I>,
+        ref_: Self::Mut<'_, I>,
         f: F,
-    ) -> Option<Self::Mut<U>>;
+    ) -> Option<Self::Mut<'_, U>>;
+
+    /// Downcast a mutable reference in a RefMut to a more specific lifetime
+    ///
+    /// This function enforces the variance of the lifetime parameter `'a` in Ref.
+    fn downcast_lifetime_mut<'a: 'b, 'b, T: ?Sized + 'static>(
+        mut_: Self::Mut<'a, T>,
+    ) -> Self::Mut<'b, T>;
 
     /// Get a mutable reference to the value. If the value has been dropped, this will panic.
     #[track_caller]
-    fn write(&mut self) -> Self::Mut<Self::Target> {
+    fn write(&mut self) -> WritableRef<'_, Self> {
         self.try_write().unwrap()
     }
 
-    /// Try to get a mutable reference to the value. If the value has been dropped, this will panic.
-    fn try_write(&mut self) -> Result<Self::Mut<Self::Target>, generational_box::BorrowMutError>;
+    /// Try to get a mutable reference to the value.
+    #[track_caller]
+    fn try_write(&mut self) -> Result<WritableRef<'_, Self>, generational_box::BorrowMutError> {
+        self.try_write_unchecked().map(Self::downcast_lifetime_mut)
+    }
+
+    /// Try to get a mutable reference to the value without checking the lifetime.
+    ///
+    /// NOTE: This method is completely safe because borrow checking is done at runtime.
+    fn try_write_unchecked(
+        &self,
+    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError>;
+
+    /// Tet a mutable reference to the value without checking the lifetime.
+    ///
+    /// NOTE: This method is completely safe because borrow checking is done at runtime.
+    fn write_unchecked(&self) -> WritableRef<'static, Self> {
+        self.try_write_unchecked().unwrap()
+    }
 
     /// Run a function with a mutable reference to the value. If the value has been dropped, this will panic.
     #[track_caller]
@@ -55,7 +82,10 @@ pub trait Writable: Readable {
 
     /// Index into the inner value and return a reference to the result.
     #[track_caller]
-    fn index_mut<I>(&mut self, index: I) -> Self::Mut<<Self::Target as std::ops::Index<I>>::Output>
+    fn index_mut<I>(
+        &mut self,
+        index: I,
+    ) -> WritableRef<'_, Self, <Self::Target as std::ops::Index<I>>::Output>
     where
         Self::Target: std::ops::IndexMut<I>,
     {
@@ -84,15 +114,14 @@ pub trait Writable: Readable {
 /// An extension trait for Writable<Option<T>> that provides some convenience methods.
 pub trait WritableOptionExt<T: 'static>: Writable<Target = Option<T>> {
     /// Gets the value out of the Option, or inserts the given value if the Option is empty.
-    fn get_or_insert(&mut self, default: T) -> Self::Mut<T> {
+    fn get_or_insert(&mut self, default: T) -> WritableRef<'_, Self, T> {
         self.get_or_insert_with(|| default)
     }
 
     /// Gets the value out of the Option, or inserts the value returned by the given function if the Option is empty.
-    fn get_or_insert_with(&mut self, default: impl FnOnce() -> T) -> Self::Mut<T> {
-        let borrow = self.read();
-        if borrow.is_none() {
-            drop(borrow);
+    fn get_or_insert_with(&mut self, default: impl FnOnce() -> T) -> WritableRef<'_, Self, T> {
+        let is_none = self.read().is_none();
+        if is_none {
             self.with_mut(|v| *v = Some(default()));
             Self::map_mut(self.write(), |v| v.as_mut().unwrap())
         } else {
@@ -102,7 +131,7 @@ pub trait WritableOptionExt<T: 'static>: Writable<Target = Option<T>> {
 
     /// Attempts to write the inner value of the Option.
     #[track_caller]
-    fn as_mut(&mut self) -> Option<Self::Mut<T>> {
+    fn as_mut(&mut self) -> Option<WritableRef<'_, Self, T>> {
         Self::try_map_mut(self.write(), |v: &mut Option<T>| v.as_mut())
     }
 }
@@ -178,36 +207,40 @@ pub trait WritableVecExt<T: 'static>: Writable<Target = Vec<T>> {
 
     /// Try to mutably get an element from the vector.
     #[track_caller]
-    fn get_mut(&mut self, index: usize) -> Option<Self::Mut<T>> {
+    fn get_mut(&mut self, index: usize) -> Option<WritableRef<'_, Self, T>> {
         Self::try_map_mut(self.write(), |v: &mut Vec<T>| v.get_mut(index))
     }
 
     /// Gets an iterator over the values of the vector.
     #[track_caller]
-    fn iter_mut(&self) -> WritableValueIterator<Self>
+    fn iter_mut(&mut self) -> WritableValueIterator<'_, Self>
     where
         Self: Sized + Clone,
     {
         WritableValueIterator {
             index: 0,
-            value: self.clone(),
+            value: self,
         }
     }
 }
 
 /// An iterator over the values of a `Writable<Vec<T>>`.
-pub struct WritableValueIterator<R> {
+pub struct WritableValueIterator<'a, R> {
     index: usize,
-    value: R,
+    value: &'a mut R,
 }
 
-impl<T: 'static, R: Writable<Target = Vec<T>>> Iterator for WritableValueIterator<R> {
-    type Item = R::Mut<T>;
+impl<'a, T: 'static, R: Writable<Target = Vec<T>>> Iterator for WritableValueIterator<'a, R> {
+    type Item = WritableRef<'a, R, T>;
 
     fn next(&mut self) -> Option<Self::Item> {
         let index = self.index;
         self.index += 1;
-        self.value.get_mut(index)
+        R::try_map_mut(
+            self.value.try_write_unchecked().unwrap(),
+            |v: &mut Vec<T>| v.get_mut(index),
+        )
+        .map(R::downcast_lifetime_mut)
     }
 }