소스 검색

document Write, GenerationalRef, and GenerationalRefMut (#3253)

Evan Almloff 6 달 전
부모
커밋
5fc099b30a
2개의 변경된 파일257개의 추가작업 그리고 5개의 파일을 삭제
  1. 149 2
      packages/generational-box/src/references.rs
  2. 108 3
      packages/signals/src/signal.rs

+ 149 - 2
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<Ref<'_, Colors>>`
+/// 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<Ref<'_, Colors>>`, found `Colors`
+///   |
+///   = note: expected struct `GenerationalRef<Ref<'_, Colors>>`
+///                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<R> {
     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<RefMut<'_, fn(u32) -> 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<RefMut<'_, ...>>`, found `Colors`
+///    |
+///    = note: expected struct `GenerationalRefMut<RefMut<'_, fn(u32) -> 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<W> {
     pub(crate) inner: W,
     pub(crate) borrow: GenerationalRefBorrowMutGuard,

+ 108 - 3
packages/signals/src/signal.rs

@@ -511,10 +511,115 @@ impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
     }
 }
 
-/// 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<dyn Any>,