diff --git a/packages/generational-box/src/references.rs b/packages/generational-box/src/references.rs index 6285ed15b..c6a5a49af 100644 --- a/packages/generational-box/src/references.rs +++ b/packages/generational-box/src/references.rs @@ -3,7 +3,82 @@ use std::{ ops::{Deref, DerefMut}, }; -/// A reference to a value in a generational box. +/// A reference to a value in a generational box. This reference acts similarly to [`std::cell::Ref`], but has extra debug information +/// to track when all references to the value are created and dropped. +/// +/// [`GenerationalRef`] implements [`Deref`] which means you can call methods on the inner value just like you would on a reference to the +/// inner value. If you need to get the inner reference directly, you can call [`GenerationalRef::deref`]. +/// +/// # Example +/// ```rust +/// # use generational_box::{Owner, UnsyncStorage, AnyStorage}; +/// let owner = UnsyncStorage::owner(); +/// let value = owner.insert(String::from("hello")); +/// let reference = value.read(); +/// +/// // You call methods like `as_str` on the reference just like you would with the inner String +/// assert_eq!(reference.as_str(), "hello"); +/// ``` +/// +/// ## Matching on GenerationalRef +/// +/// You need to get the inner reference with [`GenerationalRef::deref`] before you match the inner value. If you try to match without +/// calling [`GenerationalRef::deref`], you will get an error like this: +/// +/// ```compile_fail +/// # use generational_box::{Owner, UnsyncStorage, AnyStorage}; +/// enum Colors { +/// Red, +/// Green +/// } +/// let owner = UnsyncStorage::owner(); +/// let value = owner.insert(Colors::Red); +/// let reference = value.read(); +/// +/// match reference { +/// // Since we are matching on the `GenerationalRef` type instead of &Colors, we can't match on the enum directly +/// Colors::Red => {} +/// Colors::Green => {} +/// } +/// ``` +/// +/// ```text +/// error[E0308]: mismatched types +/// --> packages/generational-box/tests/basic.rs:25:9 +/// | +/// 2 | Red, +/// | --- unit variant defined here +/// ... +/// 3 | match reference { +/// | --------- this expression has type `GenerationalRef>` +/// 4 | // Since we are matching on the `GenerationalRef` type instead of &Colors, we can't match on the enum directly +/// 5 | Colors::Red => {} +/// | ^^^^^^^^^^^ expected `GenerationalRef>`, found `Colors` +/// | +/// = note: expected struct `GenerationalRef>` +/// found enum `Colors` +/// ``` +/// +/// Instead, you need to deref the reference to get the inner value **before** you match on it: +/// +/// ```rust +/// use std::ops::Deref; +/// # use generational_box::{AnyStorage, Owner, UnsyncStorage}; +/// enum Colors { +/// Red, +/// Green +/// } +/// let owner = UnsyncStorage::owner(); +/// let value = owner.insert(Colors::Red); +/// let reference = value.read(); +/// +/// // Deref converts the `GenerationalRef` into a `&Colors` +/// match reference.deref() { +/// // Now we can match on the inner value +/// Colors::Red => {} +/// Colors::Green => {} +/// } +/// ``` pub struct GenerationalRef { pub(crate) inner: R, guard: GenerationalRefBorrowGuard, @@ -65,7 +140,79 @@ impl Drop for GenerationalRefBorrowGuard { } } -/// A mutable reference to a value in a generational box. +/// A mutable reference to a value in a generational box. This reference acts similarly to [`std::cell::RefMut`], but has extra debug information +/// to track when all references to the value are created and dropped. +/// +/// [`GenerationalRefMut`] implements [`DerefMut`] which means you can call methods on the inner value just like you would on a mutable reference +/// to the inner value. If you need to get the inner reference directly, you can call [`GenerationalRefMut::deref_mut`]. +/// +/// # Example +/// ```rust +/// # use generational_box::{Owner, UnsyncStorage, AnyStorage}; +/// let owner = UnsyncStorage::owner(); +/// let mut value = owner.insert(String::from("hello")); +/// let mut mutable_reference = value.write(); +/// +/// // You call methods like `push_str` on the reference just like you would with the inner String +/// mutable_reference.push_str("world"); +/// ``` +/// +/// ## Matching on GenerationalMut +/// +/// You need to get the inner mutable reference with [`GenerationalRefMut::deref_mut`] before you match the inner value. If you try to match +/// without calling [`GenerationalRefMut::deref_mut`], you will get an error like this: +/// +/// ```compile_fail +/// # use generational_box::{Owner, UnsyncStorage, AnyStorage}; +/// enum Colors { +/// Red(u32), +/// Green +/// } +/// let owner = UnsyncStorage::owner(); +/// let mut value = owner.insert(Colors::Red(0)); +/// let mut mutable_reference = value.write(); +/// +/// match mutable_reference { +/// // Since we are matching on the `GenerationalRefMut` type instead of &mut Colors, we can't match on the enum directly +/// Colors::Red(brightness) => *brightness += 1, +/// Colors::Green => {} +/// } +/// ``` +/// +/// ```text +/// error[E0308]: mismatched types +/// --> packages/generational-box/tests/basic.rs:25:9 +/// | +/// 9 | match mutable_reference { +/// | ----------------- this expression has type `GenerationalRefMut Colors {Colors::Red}>>` +/// 10 | // Since we are matching on the `GenerationalRefMut` type instead of &mut Colors, we can't match on the enum directly +/// 11 | Colors::Red(brightness) => *brightness += 1, +/// | ^^^^^^^^^^^^^^^^^^^^^^^ expected `GenerationalRefMut>`, found `Colors` +/// | +/// = note: expected struct `GenerationalRefMut Colors {Colors::Red}>>` +/// found enum `Colors` +/// ``` +/// +/// Instead, you need to call deref mut on the reference to get the inner value **before** you match on it: +/// +/// ```rust +/// use std::ops::DerefMut; +/// # use generational_box::{AnyStorage, Owner, UnsyncStorage}; +/// enum Colors { +/// Red(u32), +/// Green +/// } +/// let owner = UnsyncStorage::owner(); +/// let mut value = owner.insert(Colors::Red(0)); +/// let mut mutable_reference = value.write(); +/// +/// // DerefMut converts the `GenerationalRefMut` into a `&mut Colors` +/// match mutable_reference.deref_mut() { +/// // Now we can match on the inner value +/// Colors::Red(brightness) => *brightness += 1, +/// Colors::Green => {} +/// } +/// ``` pub struct GenerationalRefMut { pub(crate) inner: W, pub(crate) borrow: GenerationalRefBorrowMutGuard, diff --git a/packages/signals/src/signal.rs b/packages/signals/src/signal.rs index bf39dfe7b..c84b4b03e 100644 --- a/packages/signals/src/signal.rs +++ b/packages/signals/src/signal.rs @@ -511,10 +511,115 @@ impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage>> } } -/// A mutable reference to a signal's value. +/// A mutable reference to a signal's value. This reference acts similarly to [`std::cell::RefMut`], but it has extra debug information +/// and integrates with the reactive system to automatically update dependents. /// -/// T is the current type of the write -/// S is the storage type of the signal +/// [`Write`] implements [`DerefMut`] which means you can call methods on the inner value just like you would on a mutable reference +/// to the inner value. If you need to get the inner reference directly, you can call [`Write::deref_mut`]. +/// +/// # Example +/// ```rust +/// # use dioxus::prelude::*; +/// fn app() -> Element { +/// let mut value = use_signal(|| String::from("hello")); +/// +/// rsx! { +/// button { +/// onclick: move |_| { +/// let mut mutable_reference = value.write(); +/// +/// // You call methods like `push_str` on the reference just like you would with the inner String +/// mutable_reference.push_str("world"); +/// }, +/// "Click to add world to the string" +/// } +/// div { "{value}" } +/// } +/// } +/// ``` +/// +/// ## Matching on Write +/// +/// You need to get the inner mutable reference with [`Write::deref_mut`] before you match the inner value. If you try to match +/// without calling [`Write::deref_mut`], you will get an error like this: +/// +/// ```compile_fail +/// # use dioxus::prelude::*; +/// #[derive(Debug)] +/// enum Colors { +/// Red(u32), +/// Green +/// } +/// fn app() -> Element { +/// let mut value = use_signal(|| Colors::Red(0)); +/// +/// rsx! { +/// button { +/// onclick: move |_| { +/// let mut mutable_reference = value.write(); +/// +/// match mutable_reference { +/// // Since we are matching on the `Write` type instead of &mut Colors, we can't match on the enum directly +/// Colors::Red(brightness) => *brightness += 1, +/// Colors::Green => {} +/// } +/// }, +/// "Click to add brightness to the red color" +/// } +/// div { "{value:?}" } +/// } +/// } +/// ``` +/// +/// ```text +/// error[E0308]: mismatched types +/// --> src/main.rs:18:21 +/// | +/// 16 | match mutable_reference { +/// | ----------------- this expression has type `dioxus::prelude::Write<'_, Colors>` +/// 17 | // Since we are matching on the `Write` t... +/// 18 | Colors::Red(brightness) => *brightness += 1, +/// | ^^^^^^^^^^^^^^^^^^^^^^^ expected `Write<'_, Colors>`, found `Colors` +/// | +/// = note: expected struct `dioxus::prelude::Write<'_, Colors, >` +/// found enum `Colors` +/// ``` +/// +/// Instead, you need to call deref mut on the reference to get the inner value **before** you match on it: +/// +/// ```rust +/// use std::ops::DerefMut; +/// # use dioxus::prelude::*; +/// #[derive(Debug)] +/// enum Colors { +/// Red(u32), +/// Green +/// } +/// fn app() -> Element { +/// let mut value = use_signal(|| Colors::Red(0)); +/// +/// rsx! { +/// button { +/// onclick: move |_| { +/// let mut mutable_reference = value.write(); +/// +/// // DerefMut converts the `Write` into a `&mut Colors` +/// match mutable_reference.deref_mut() { +/// // Now we can match on the inner value +/// Colors::Red(brightness) => *brightness += 1, +/// Colors::Green => {} +/// } +/// }, +/// "Click to add brightness to the red color" +/// } +/// div { "{value:?}" } +/// } +/// } +/// ``` +/// +/// ## Generics +/// - T is the current type of the write +/// - S is the storage type of the signal. This type determines if the signal is local to the current thread, or it can be shared across threads. pub struct Write<'a, T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> { write: S::Mut<'a, T>, drop_signal: Box,