docs: StoredValue methods and examples for 0.7 (#2817)

This commit is contained in:
Chris 2024-08-13 17:38:07 -04:00 committed by GitHub
parent 17a150b3bf
commit b9dfd9a5ae
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 243 additions and 29 deletions

View file

@ -261,27 +261,81 @@ where
} }
impl<T, S: Storage<T>> StoredValue<T, S> { impl<T, S: Storage<T>> StoredValue<T, S> {
/// Same as [`StoredValue::with_value`] but returns `Some(O)` only if /// Returns an [`Option`] of applying a function to the value within the [`StoredValue`].
/// the stored value has not yet been disposed, `None` otherwise. ///
/// If the owner of the reactive node has not been disposed [`Some`] is returned. Calling this
/// function after the owner has been disposed will always return [`None`].
///
/// See [`StoredValue::with_value`] for a version that panics in the case of the owner being
/// disposed.
///
/// # Examples
/// ```rust
/// # use reactive_graph::owner::StoredValue;
/// # use reactive_graph::traits::Dispose;
///
/// // Does not implement Clone
/// struct Data {
/// rows: Vec<u8>,
/// }
///
/// let data = StoredValue::new(Data {
/// rows: vec![0, 1, 2, 3, 4],
/// });
///
/// // Easy to move into closures because StoredValue is Copy + 'static.
/// // *NOTE* this is not the same thing as a derived signal!
/// // *NOTE* this will not be automatically rerun as StoredValue is NOT reactive!
/// let length_fn = move || data.try_with_value(|inner| inner.rows.len());
///
/// let sum = data.try_with_value(|inner| inner.rows.iter().sum::<u8>());
///
/// assert_eq!(sum, Some(10));
/// assert_eq!(length_fn(), Some(5));
///
/// // You should not call dispose yourself in normal user code.
/// // This is shown here for the sake of the example.
/// data.dispose();
///
/// let last = data.try_with_value(|inner| inner.rows.last().cloned());
///
/// assert_eq!(last, None);
/// assert_eq!(length_fn(), None);
/// ```
pub fn try_with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> Option<U> { pub fn try_with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> Option<U> {
S::try_with(self.node, fun) S::try_with(self.node, fun)
} }
/// Applies a function to the current stored value and returns the result. /// Returns the output of applying a function to the value within the [`StoredValue`].
/// ///
/// # Panics /// # Panics
/// Panics if you try to access a value owned by a reactive node that has been disposed. ///
/// This function panics when called after the owner of the reactive node has been disposed.
/// See [`StoredValue::try_with_value`] for a version without panic.
/// ///
/// # Examples /// # Examples
/// ``` /// ```rust
/// # use reactive_graph::owner::StoredValue; /// # use reactive_graph::owner::StoredValue;
/// pub struct MyUncloneableData {
/// pub value: String,
/// }
/// let data = StoredValue::new(MyUncloneableData { value: "a".into() });
/// ///
/// // calling .with_value() to extract the value /// // Does not implement Clone
/// data.with_value(|data| assert_eq!(data.value, "a")); /// struct Data {
/// rows: Vec<u8>,
/// }
///
/// let data = StoredValue::new(Data {
/// rows: vec![1, 2, 3],
/// });
///
/// // Easy to move into closures because StoredValue is Copy + 'static.
/// // *NOTE* this is not the same thing as a derived signal!
/// // *NOTE* this will not be automatically rerun as StoredValue is NOT reactive!
/// let length_fn = move || data.with_value(|inner| inner.rows.len());
///
/// let sum = data.with_value(|inner| inner.rows.iter().sum::<u8>());
///
/// assert_eq!(sum, 6);
/// assert_eq!(length_fn(), 3);
/// ```
pub fn with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> U { pub fn with_value<U>(&self, fun: impl FnOnce(&T) -> U) -> U {
self.try_with_value(fun) self.try_with_value(fun)
.unwrap_or_else(unwrap_signal!(self)) .unwrap_or_else(unwrap_signal!(self))
@ -296,28 +350,126 @@ impl<T, S: Storage<T>> StoredValue<T, S> {
S::try_with_mut(self.node, fun) S::try_with_mut(self.node, fun)
} }
/// Updates the stored value by applying the given closure. /// Updates the value within [`StoredValue`] by applying a function to it.
/// ///
/// ## Examples /// # Panics
/// ``` /// This function panics when called after the owner of the reactive node has been disposed.
/// See [`StoredValue::try_update_value`] for a version without panic.
///
/// # Examples
/// ```rust
/// # use reactive_graph::owner::StoredValue; /// # use reactive_graph::owner::StoredValue;
/// pub struct MyUncloneableData { ///
/// pub value: String, /// #[derive(Default)] // Does not implement Clone
/// struct Data {
/// rows: Vec<u8>,
/// } /// }
/// let data = StoredValue::new(MyUncloneableData { value: "a".into() }); ///
/// data.update_value(|data| data.value = "b".into()); /// let data = StoredValue::new(Data::default());
/// assert_eq!(data.with_value(|data| data.value.clone()), "b"); ///
/// // Easy to move into closures because StoredValue is Copy + 'static.
/// // *NOTE* this is not the same thing as a derived signal!
/// // *NOTE* this will not be automatically rerun as StoredValue is NOT reactive!
/// let push_next = move || {
/// data.update_value(|inner| match inner.rows.last().as_deref() {
/// Some(n) => inner.rows.push(n + 1),
/// None => inner.rows.push(0),
/// })
/// };
///
/// data.update_value(|inner| inner.rows = vec![5, 6, 7]);
/// data.with_value(|inner| assert_eq!(inner.rows.last(), Some(&7)));
///
/// push_next();
/// data.with_value(|inner| assert_eq!(inner.rows.last(), Some(&8)));
///
/// data.update_value(|inner| {
/// std::mem::take(inner) // sets Data back to default
/// });
/// data.with_value(|inner| assert!(inner.rows.is_empty()));
/// ``` /// ```
pub fn update_value<U>(&self, fun: impl FnOnce(&mut T) -> U) { pub fn update_value<U>(&self, fun: impl FnOnce(&mut T) -> U) {
self.try_update_value(fun); self.try_update_value(fun);
} }
/// Tries to set the value. If the value has been disposed, returns `Some(value)`. /// Sets the value within [`StoredValue`].
///
/// Returns [`Some`] containing the passed value if the owner of the reactive node has been
/// disposed.
///
/// For types that do not implement [`Clone`], or in cases where allocating the entire object
/// would be too expensive, prefer [`StoredValue::try_update_value`].
///
/// # Examples
/// ```rust
/// # use reactive_graph::owner::StoredValue;
/// # use reactive_graph::traits::Dispose;
///
/// let data = StoredValue::new(String::default());
///
/// // Easy to move into closures because StoredValue is Copy + 'static.
/// // *NOTE* this is not the same thing as a derived signal!
/// // *NOTE* this will not be automatically rerun as StoredValue is NOT reactive!
/// let say_hello = move || {
/// // Note that using the `update` methods would be more efficient here.
/// data.try_set_value("Hello, World!".into())
/// };
/// // *NOTE* this is not the same thing as a derived signal!
/// // *NOTE* this will not be automatically rerun as StoredValue is NOT reactive!
/// let reset = move || {
/// // Note that using the `update` methods would be more efficient here.
/// data.try_set_value(Default::default())
/// };
/// assert_eq!(data.get_value(), "");
///
/// // None is returned because the value was able to be updated
/// assert_eq!(say_hello(), None);
///
/// assert_eq!(data.get_value(), "Hello, World!");
///
/// reset();
/// assert_eq!(data.get_value(), "");
///
/// // You should not call dispose yourself in normal user code.
/// // This is shown here for the sake of the example.
/// data.dispose();
///
/// // The reactive owner is disposed, so the value we intended to set is now
/// // returned as some.
/// assert_eq!(say_hello().as_deref(), Some("Hello, World!"));
/// assert_eq!(reset().as_deref(), Some(""));
/// ```
pub fn try_set_value(&self, value: T) -> Option<T> { pub fn try_set_value(&self, value: T) -> Option<T> {
S::try_set(self.node, value) S::try_set(self.node, value)
} }
/// Sets the value to a new value. /// Sets the value within [`StoredValue`].
///
/// For types that do not implement [`Clone`], or in cases where allocating the entire object
/// would be too expensive, prefer [`StoredValue::update_value`].
///
/// # Panics
/// This function panics when called after the owner of the reactive node has been disposed.
/// See [`StoredValue::try_set_value`] for a version without panic.
///
/// # Examples
/// ```rust
/// # use reactive_graph::owner::StoredValue;
///
/// let data = StoredValue::new(10);
///
/// // Easy to move into closures because StoredValue is Copy + 'static.
/// // *NOTE* this is not the same thing as a derived signal!
/// // *NOTE* this will not be automatically rerun as StoredValue is NOT reactive!
/// let maxout = move || data.set_value(u8::MAX);
/// let zero = move || data.set_value(u8::MIN);
///
/// maxout();
/// assert_eq!(data.get_value(), u8::MAX);
///
/// zero();
/// assert_eq!(data.get_value(), u8::MIN);
/// ```
pub fn set_value(&self, value: T) { pub fn set_value(&self, value: T) {
self.update_value(|n| *n = value); self.update_value(|n| *n = value);
} }
@ -333,15 +485,75 @@ impl<T, S: Storage<T>> StoredValue<T, S>
where where
T: Clone + 'static, T: Clone + 'static,
{ {
/// Clones and returns the current value, or `None` if it has already been disposed. /// Returns the value within [`StoredValue`] by cloning.
///
/// Returns [`Some`] containing the value if the owner of the reactive node has not been
/// disposed. When disposed, returns [`None`].
///
/// See [`StoredValue::try_with_value`] for a version that avoids cloning. See
/// [`StoredValue::get_value`] for a version that clones, but panics if the node is disposed.
///
/// # Examples
/// ```rust
/// # use reactive_graph::owner::StoredValue;
/// # use reactive_graph::traits::Dispose;
///
/// // u8 is practically free to clone.
/// let data: StoredValue<u8> = StoredValue::new(10);
///
/// // Larger data structures can become very expensive to clone.
/// // You may prefer to use StoredValue::try_with_value.
/// let _expensive: StoredValue<Vec<String>> = StoredValue::new(vec![]);
///
/// // Easy to move into closures because StoredValue is Copy + 'static
/// let maxout = move || data.set_value(u8::MAX);
/// let zero = move || data.set_value(u8::MIN);
///
/// maxout();
/// assert_eq!(data.try_get_value(), Some(u8::MAX));
///
/// zero();
/// assert_eq!(data.try_get_value(), Some(u8::MIN));
///
/// // You should not call dispose yourself in normal user code.
/// // This is shown here for the sake of the example.
/// data.dispose();
///
/// assert_eq!(data.try_get_value(), None);
/// ```
pub fn try_get_value(&self) -> Option<T> { pub fn try_get_value(&self) -> Option<T> {
self.try_with_value(T::clone) self.try_with_value(T::clone)
} }
/// Clones and returns the current value. /// Returns the value within [`StoredValue`] by cloning.
///
/// See [`StoredValue::with_value`] for a version that avoids cloning.
/// ///
/// # Panics /// # Panics
/// Panics if you try to access a value owned by a reactive node that has been disposed. /// This function panics when called after the owner of the reactive node has been disposed.
/// See [`StoredValue::try_get_value`] for a version without panic.
///
/// # Examples
/// ```rust
/// # use reactive_graph::owner::StoredValue;
///
/// // u8 is practically free to clone.
/// let data: StoredValue<u8> = StoredValue::new(10);
///
/// // Larger data structures can become very expensive to clone.
/// // You may prefer to use StoredValue::try_with_value.
/// let _expensive: StoredValue<Vec<String>> = StoredValue::new(vec![]);
///
/// // Easy to move into closures because StoredValue is Copy + 'static
/// let maxout = move || data.set_value(u8::MAX);
/// let zero = move || data.set_value(u8::MIN);
///
/// maxout();
/// assert_eq!(data.get_value(), u8::MAX);
///
/// zero();
/// assert_eq!(data.get_value(), u8::MIN);
/// ```
pub fn get_value(&self) -> T { pub fn get_value(&self) -> T {
self.with_value(T::clone) self.with_value(T::clone)
} }
@ -356,9 +568,11 @@ impl<T, S> Dispose for StoredValue<T, S> {
/// Creates a new [`StoredValue`]. /// Creates a new [`StoredValue`].
#[inline(always)] #[inline(always)]
#[track_caller] #[track_caller]
#[deprecated = "This function is being removed to conform to Rust idioms. \ #[deprecated(
Please use `StoredValue::new()` or `StoredValue::new_local()` \ since = "0.7.0-beta",
instead."] note = "This function is being removed to conform to Rust idioms. Please \
use `StoredValue::new()` or `StoredValue::new_local()` instead."
)]
pub fn store_value<T>(value: T) -> StoredValue<T> pub fn store_value<T>(value: T) -> StoredValue<T>
where where
T: Send + Sync + 'static, T: Send + Sync + 'static,

View file

@ -439,7 +439,7 @@ where
} }
/// A mapped read guard in which the mapping function is a closure. If the mapping function is a /// A mapped read guard in which the mapping function is a closure. If the mapping function is a
/// function pointed, use [`Mapped`]. /// function pointer, use [`Mapped`].
pub struct MappedArc<Inner, U> pub struct MappedArc<Inner, U>
where where
Inner: Deref, Inner: Deref,
@ -518,7 +518,7 @@ where
} }
/// A mapped write guard in which the mapping function is a closure. If the mapping function is a /// A mapped write guard in which the mapping function is a closure. If the mapping function is a
/// function pointed, use [`MappedMut`]. /// function pointer, use [`MappedMut`].
pub struct MappedMutArc<Inner, U> pub struct MappedMutArc<Inner, U>
where where
Inner: Deref, Inner: Deref,