Stores: Externally tracked state and reactive collections (#4483)

* map signals mutably

* boxed signals

* remove mutable borrow type from boxed writable

* improve map mut example

* restore boxed_mut helper

* make boxed readable/writable types copy

* Move away from the ReadOnlySignal alias in examples

* automatically convert Write

* Fix some tests

* ReadSignal and WriteSignal

* move read only signal alias

* make maps copy

* store prototype

* implement derive macro

* Handle generics in the derive macro

* Make Selector boxable

* derive boxed conversion

* Get rid of store type

* Remove hardcoded type from derive macro

* selector -> store

* Refactor store implementation

* more vec methods

* todo mvc store example

* Implement Storable for HashMap

* fix write

* restore sorting logic in todomvc

* derive readable and writable

* improve todomvc store example

* Result store

* Option store

* collect dead subscribers

* fix clippy and formatting

* recognize some common foreign types automatically

* fix foreign stores in props

* slice and array selectors

* clean up store example

* writable is child owned

* box selector

* read and write to builtins

* re-use the proc macro in the derive macro

* Switch to a extension trait based approach

* switch to extension traits in the derive macro

* transposed method

* derive enums

* Expose Store::new

* remove stores from the prelude

* More consistent naming scheme for SelectorScope methods

* document hashmap methods

* document index extension

* document option ext

* document result ext

* document slice extension

* document the vec extension

* make more methods borrow the store mutably

* Move to impls to have higher priority over the builtin write extension traits

* fix deadlock

* finish stores documentation

* Document the store macro

* cleanup docs and organization

* finish derive docs

* fix typo

* fix clippy

* fix doc tests

* fix derive macro with generics

* fix clippy

* test and fix empty structs

* clean up todomvc store example

* default to a boxed store writer to make it easier to type when providing to the context api

* fix cargo doc

* fix clippy in store example

* relax generational box lifetime bounds

* relax bounds

* relax signal bounds

* fix compilation

* read store

* relax bounds on default store impls

* fix enum derive

* Convert to readsignal and writesignal automatically

* simplify type matching logic

* add write store

* unsized stores

* shrink the path vec type

* use more concrete types

* remove opaque types

* remove copy bounds

* use lens as generic name everywhere

* more consistent borrow semantics

* use a concrete type for the signal metadata and mention references in the map docs

* improve todomvc store docs

* document store derive macro internals

* refactor macro

* improve selector node documentation

* fix clippy

* add btreemap

* fix doc tests

* more option methods

* fix doc

* fix clippy

* add a bunch more result methods

* add a bunch of bounds to the iterators

* fix clippy

* remove option from subscribers

* add store macro to generate extension traits

* fix store example

* fix deadlock

* fix docs

* fix doc tests

* fix clippy

* add stores to public api

* add store macro, and then update todomvc_store example

---------

Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
Evan AlmloffandJonathan Kelley authored and GitHub committed 2025-08-05 11:30:21 -07:00
1 parent 5591070e92
commit 58c41f8561
57 files changed
+4958 -436

No files matched your search

+97 -80
View File
@@ -12,7 +12,7 @@ use crate::{
pub type ReadOnlySignal<T> = ReadSignal<T>;
/// A boxed version of [Readable] that can be used to store any readable type.
pub struct ReadSignal<T: ?Sized + 'static> {
pub struct ReadSignal<T: ?Sized> {
value: CopyValue<Box<dyn Readable<Target = T, Storage = UnsyncStorage>>>,
}
@@ -25,45 +25,42 @@ impl<T: ?Sized + 'static> ReadSignal<T> {
}
/// Point to another [ReadSignal]. This will subscribe the other [ReadSignal] to all subscribers of this [ReadSignal].
pub fn point_to(&self, other: Self) -> BorrowResult
where
T: Sized + 'static,
{
#[allow(clippy::mutable_key_type)]
pub fn point_to(&self, other: Self) -> BorrowResult {
let this_subscribers = self.subscribers();
let mut this_subscribers_vec = Vec::new();
// Note we don't subscribe directly in the visit closure to avoid a deadlock when pointing to self
this_subscribers.visit(|subscriber| this_subscribers_vec.push(*subscriber));
let other_subscribers = other.subscribers();
if let (Some(this_subscribers), Some(other_subscribers)) =
(this_subscribers, other_subscribers)
{
this_subscribers.visit(|subscriber| {
subscriber.subscribe(other_subscribers.clone());
});
for subscriber in this_subscribers_vec {
subscriber.subscribe(other_subscribers.clone());
}
self.value.point_to(other.value)
self.value.point_to(other.value)?;
Ok(())
}
#[doc(hidden)]
/// This is only used by the `props` macro.
/// Mark any readers of the signal as dirty
pub fn mark_dirty(&mut self) {
let subscribers = self.value.subscribers();
if let Some(subscribers) = subscribers {
subscribers.visit(|subscriber| {
subscriber.mark_dirty();
});
let subscribers = self.subscribers();
let mut this_subscribers_vec = Vec::new();
subscribers.visit(|subscriber| this_subscribers_vec.push(*subscriber));
for subscriber in this_subscribers_vec {
subscribers.remove(&subscriber);
subscriber.mark_dirty();
}
}
}
impl<T: ?Sized + 'static> Clone for ReadSignal<T> {
impl<T: ?Sized> Clone for ReadSignal<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: ?Sized + 'static> Copy for ReadSignal<T> {}
impl<T: ?Sized> Copy for ReadSignal<T> {}
impl<T: ?Sized + 'static> PartialEq for ReadSignal<T> {
impl<T: ?Sized> PartialEq for ReadSignal<T> {
fn eq(&self, other: &Self) -> bool {
self.value == other.value
}
@@ -103,14 +100,17 @@ impl<T: Clone + 'static> Deref for ReadSignal<T> {
}
}
impl<T: 'static> Readable for ReadSignal<T> {
impl<T: ?Sized> Readable for ReadSignal<T> {
type Target = T;
type Storage = UnsyncStorage;
#[track_caller]
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
T: 'static,
{
self.value
.try_peek_unchecked()
.unwrap()
@@ -118,39 +118,46 @@ impl<T: 'static> Readable for ReadSignal<T> {
}
#[track_caller]
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
where
T: 'static,
{
self.value
.try_peek_unchecked()
.unwrap()
.try_peek_unchecked()
}
fn subscribers(&self) -> Option<Subscribers> {
self.value.subscribers()
fn subscribers(&self) -> Subscribers
where
T: 'static,
{
self.value.try_peek_unchecked().unwrap().subscribers()
}
}
// We can't implement From<impl Readable<Target = T, Storage = S> + 'static> for ReadSignal<T, S>
// We can't implement From<impl Readable<Target = T, Storage = S> > for ReadSignal<T, S>
// because it would conflict with the From<T> for T implementation, but we can implement it for
// all specific readable types
impl<T> From<Signal<T>> for ReadSignal<T> {
impl<T: 'static> From<Signal<T>> for ReadSignal<T> {
fn from(value: Signal<T>) -> Self {
Self::new(value)
}
}
impl<T: PartialEq> From<Memo<T>> for ReadSignal<T> {
impl<T: PartialEq + 'static> From<Memo<T>> for ReadSignal<T> {
fn from(value: Memo<T>) -> Self {
Self::new(value)
}
}
impl<T> From<CopyValue<T>> for ReadSignal<T> {
impl<T: 'static> From<CopyValue<T>> for ReadSignal<T> {
fn from(value: CopyValue<T>) -> Self {
Self::new(value)
}
}
impl<T: Clone + 'static, R: 'static> From<Global<T, R>> for ReadSignal<R>
impl<T, R> From<Global<T, R>> for ReadSignal<R>
where
T: Readable<Target = R, Storage = UnsyncStorage> + InitializeFromFunction<R>,
T: Readable<Target = R, Storage = UnsyncStorage> + InitializeFromFunction<R> + Clone + 'static,
R: 'static,
{
fn from(value: Global<T, R>) -> Self {
Self::new(value)
@@ -158,7 +165,7 @@ where
}
impl<V, O, F> From<MappedSignal<O, V, F>> for ReadSignal<O>
where
O: ?Sized,
O: ?Sized + 'static,
V: Readable<Storage = UnsyncStorage> + 'static,
F: Fn(&V::Target) -> &O + 'static,
{
@@ -168,7 +175,7 @@ where
}
impl<V, O, F, FMut> From<MappedMutSignal<O, V, F, FMut>> for ReadSignal<O>
where
O: ?Sized,
O: ?Sized + 'static,
V: Readable<Storage = UnsyncStorage> + 'static,
F: Fn(&V::Target) -> &O + 'static,
FMut: 'static,
@@ -177,18 +184,23 @@ where
Self::new(value)
}
}
impl<T: ?Sized + 'static> From<WriteSignal<T>> for ReadSignal<T> {
fn from(value: WriteSignal<T>) -> Self {
Self::new(value)
}
}
/// A boxed version of [Writable] that can be used to store any writable type.
pub struct WriteSignal<T: ?Sized + 'static> {
pub struct WriteSignal<T: ?Sized> {
value: CopyValue<
Box<dyn Writable<Target = T, Storage = UnsyncStorage, WriteMetadata = Box<dyn Any>>>,
>,
}
impl<T: ?Sized> WriteSignal<T> {
impl<T: ?Sized + 'static> WriteSignal<T> {
/// Create a new boxed writable value.
pub fn new<M>(
value: impl Writable<Target = T, Storage = UnsyncStorage, WriteMetadata = M> + 'static,
pub fn new(
value: impl Writable<Target = T, Storage = UnsyncStorage, WriteMetadata: 'static> + 'static,
) -> Self {
Self {
value: CopyValue::new(Box::new(BoxWriteMetadata::new(value))),
@@ -213,51 +225,47 @@ impl<W: Readable> Readable for BoxWriteMetadata<W> {
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
W::Target: 'static,
{
self.value.try_read_unchecked()
}
fn try_peek_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
W::Target: 'static,
{
self.value.try_peek_unchecked()
}
fn subscribers(&self) -> Option<Subscribers> {
fn subscribers(&self) -> Subscribers
where
W::Target: 'static,
{
self.value.subscribers()
}
}
impl<W: Writable> Writable for BoxWriteMetadata<W> {
impl<W> Writable for BoxWriteMetadata<W>
where
W: Writable,
W::WriteMetadata: 'static,
{
type WriteMetadata = Box<dyn Any>;
fn try_write_unchecked(
&self,
) -> Result<crate::WritableRef<'static, Self>, generational_box::BorrowMutError> {
) -> Result<crate::WritableRef<'static, Self>, generational_box::BorrowMutError>
where
W::Target: 'static,
{
self.value
.try_write_unchecked()
.map(|w| w.map_metadata(|data| Box::new(data) as Box<dyn Any>))
}
fn write(&mut self) -> crate::WritableRef<'_, Self> {
self.value
.write()
.map_metadata(|data| Box::new(data) as Box<dyn Any>)
}
fn try_write(
&mut self,
) -> Result<crate::WritableRef<'_, Self>, generational_box::BorrowMutError> {
self.value
.try_write()
.map(|w| w.map_metadata(|data| Box::new(data) as Box<dyn Any>))
}
fn write_unchecked(&self) -> crate::WritableRef<'static, Self> {
self.value
.write_unchecked()
.map_metadata(|data| Box::new(data) as Box<dyn Any>)
}
}
impl<T: ?Sized> Clone for WriteSignal<T> {
@@ -303,14 +311,17 @@ impl<T: Clone + 'static> Deref for WriteSignal<T> {
}
}
impl<T: 'static> Readable for WriteSignal<T> {
impl<T: ?Sized> Readable for WriteSignal<T> {
type Target = T;
type Storage = UnsyncStorage;
#[track_caller]
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
T: 'static,
{
self.value
.try_peek_unchecked()
.unwrap()
@@ -318,28 +329,33 @@ impl<T: 'static> Readable for WriteSignal<T> {
}
#[track_caller]
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
where
T: 'static,
{
self.value
.try_peek_unchecked()
.unwrap()
.try_peek_unchecked()
}
fn subscribers(&self) -> Option<Subscribers> {
self.value.subscribers()
fn subscribers(&self) -> Subscribers
where
T: 'static,
{
self.value.try_peek_unchecked().unwrap().subscribers()
}
}
impl<T: 'static> Writable for WriteSignal<T> {
impl<T: ?Sized> Writable for WriteSignal<T> {
type WriteMetadata = Box<dyn Any>;
fn write_unchecked(&self) -> crate::WritableRef<'static, Self> {
self.value.try_peek_unchecked().unwrap().write_unchecked()
}
fn try_write_unchecked(
&self,
) -> Result<crate::WritableRef<'static, Self>, generational_box::BorrowMutError> {
) -> Result<crate::WritableRef<'static, Self>, generational_box::BorrowMutError>
where
T: 'static,
{
self.value
.try_peek_unchecked()
.unwrap()
@@ -347,22 +363,23 @@ impl<T: 'static> Writable for WriteSignal<T> {
}
}
// We can't implement From<impl Writable<Target = T, Storage = S> + 'static> for Write<T, S>
// We can't implement From<impl Writable<Target = T, Storage = S>> for Write<T, S>
// because it would conflict with the From<T> for T implementation, but we can implement it for
// all specific readable types
impl<T> From<Signal<T>> for WriteSignal<T> {
impl<T: 'static> From<Signal<T>> for WriteSignal<T> {
fn from(value: Signal<T>) -> Self {
Self::new(value)
}
}
impl<T> From<CopyValue<T>> for WriteSignal<T> {
impl<T: 'static> From<CopyValue<T>> for WriteSignal<T> {
fn from(value: CopyValue<T>) -> Self {
Self::new(value)
}
}
impl<T: Clone + 'static, R: 'static> From<Global<T, R>> for WriteSignal<R>
impl<T, R> From<Global<T, R>> for WriteSignal<R>
where
T: Writable<Target = R, Storage = UnsyncStorage> + InitializeFromFunction<R>,
T: Writable<Target = R, Storage = UnsyncStorage> + InitializeFromFunction<R> + Clone + 'static,
R: 'static,
{
fn from(value: Global<T, R>) -> Self {
Self::new(value)
@@ -370,7 +387,7 @@ where
}
impl<V, O, F, FMut> From<MappedMutSignal<O, V, F, FMut>> for WriteSignal<O>
where
O: ?Sized,
O: ?Sized + 'static,
V: Writable<Storage = UnsyncStorage> + 'static,
F: Fn(&V::Target) -> &O + 'static,
FMut: Fn(&mut V::Target) -> &mut O + 'static,
+30 -19
View File
@@ -3,7 +3,9 @@
use dioxus_core::Subscribers;
use dioxus_core::{current_owner, current_scope_id, ScopeId};
use generational_box::{BorrowResult, GenerationalBox, GenerationalBoxId, Storage, UnsyncStorage};
use generational_box::{
AnyStorage, BorrowResult, GenerationalBox, GenerationalBoxId, Storage, UnsyncStorage,
};
use std::ops::Deref;
use crate::read_impls;
@@ -18,15 +20,15 @@ use crate::{default_impl, write_impls, WritableExt};
/// CopyValue is a wrapper around a value to make the value mutable and Copy.
///
/// It is internally backed by [`generational_box::GenerationalBox`].
pub struct CopyValue<T: 'static, S: Storage<T> = UnsyncStorage> {
pub struct CopyValue<T, S: 'static = UnsyncStorage> {
pub(crate) value: GenerationalBox<T, S>,
pub(crate) origin_scope: ScopeId,
}
#[cfg(feature = "serialize")]
impl<T: 'static, Store: Storage<T>> serde::Serialize for CopyValue<T, Store>
impl<T, Store: Storage<T>> serde::Serialize for CopyValue<T, Store>
where
T: serde::Serialize,
T: serde::Serialize + 'static,
{
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
self.value.read().serialize(serializer)
@@ -34,9 +36,9 @@ where
}
#[cfg(feature = "serialize")]
impl<'de, T: 'static, Store: Storage<T>> serde::Deserialize<'de> for CopyValue<T, Store>
impl<'de, T, Store: Storage<T>> serde::Deserialize<'de> for CopyValue<T, Store>
where
T: serde::Deserialize<'de>,
T: serde::Deserialize<'de> + 'static,
{
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let value = T::deserialize(deserializer)?;
@@ -61,17 +63,23 @@ impl<T: 'static> CopyValue<T> {
}
}
impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
impl<T, S: Storage<T>> CopyValue<T, S> {
/// Create a new CopyValue. The value will be stored in the current component.
///
/// Once the component this value is created in is dropped, the value will be dropped.
#[track_caller]
pub fn new_maybe_sync(value: T) -> Self {
pub fn new_maybe_sync(value: T) -> Self
where
T: 'static,
{
Self::new_with_caller(value, std::panic::Location::caller())
}
/// Create a new CopyValue without an owner. This will leak memory if you don't manually drop it.
pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self
where
T: 'static,
{
Self {
value: GenerationalBox::leak(value, caller),
origin_scope: current_scope_id().expect("in a virtual dom"),
@@ -117,7 +125,10 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
}
/// Manually drop the value in the CopyValue, invalidating the value in the process.
pub fn manually_drop(&self) {
pub fn manually_drop(&self)
where
T: 'static,
{
self.value.manually_drop()
}
@@ -137,7 +148,7 @@ impl<T: 'static, S: Storage<T>> CopyValue<T, S> {
}
}
impl<T: 'static, S: Storage<T>> Readable for CopyValue<T, S> {
impl<T, S: Storage<T>> Readable for CopyValue<T, S> {
type Target = T;
type Storage = S;
@@ -155,12 +166,12 @@ impl<T: 'static, S: Storage<T>> Readable for CopyValue<T, S> {
self.value.try_read()
}
fn subscribers(&self) -> Option<Subscribers> {
None
fn subscribers(&self) -> Subscribers {
Subscribers::new_noop()
}
}
impl<T: 'static, S: Storage<T>> Writable for CopyValue<T, S> {
impl<T, S: Storage<T>> Writable for CopyValue<T, S> {
type WriteMetadata = ();
#[track_caller]
@@ -172,14 +183,14 @@ impl<T: 'static, S: Storage<T>> Writable for CopyValue<T, S> {
}
}
impl<T: 'static, S: Storage<T>> PartialEq for CopyValue<T, S> {
impl<T, S: AnyStorage> PartialEq for CopyValue<T, S> {
fn eq(&self, other: &Self) -> bool {
self.value.ptr_eq(&other.value)
}
}
impl<T: 'static, S: Storage<T>> Eq for CopyValue<T, S> {}
impl<T, S: AnyStorage> Eq for CopyValue<T, S> {}
impl<T: Copy, S: Storage<T>> Deref for CopyValue<T, S> {
impl<T: Copy + 'static, S: Storage<T>> Deref for CopyValue<T, S> {
type Target = dyn Fn() -> T;
fn deref(&self) -> &Self::Target {
@@ -187,13 +198,13 @@ impl<T: Copy, S: Storage<T>> Deref for CopyValue<T, S> {
}
}
impl<T, S: Storage<T>> Clone for CopyValue<T, S> {
impl<T, S> Clone for CopyValue<T, S> {
fn clone(&self) -> Self {
*self
}
}
impl<T, S: Storage<T>> Copy for CopyValue<T, S> {}
impl<T, S> Copy for CopyValue<T, S> {}
read_impls!(CopyValue<T, S: Storage<T>>);
default_impl!(CopyValue<T, S: Storage<T>>);
+1 -1
View File
@@ -3,7 +3,7 @@ use crate::read::ReadableExt;
use crate::read_impls;
use crate::Memo;
impl<T: PartialEq> InitializeFromFunction<T> for Memo<T> {
impl<T: PartialEq + 'static> InitializeFromFunction<T> for Memo<T> {
fn initialize_from_function(f: fn() -> T) -> Self {
Memo::new(f)
}
+31 -15
View File
@@ -32,9 +32,10 @@ pub struct Global<T, R = 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>
impl<T: Clone, R: Clone> Deref for Global<T, R>
where
T: Readable<Target = R> + InitializeFromFunction<R>,
T: Readable<Target = R> + InitializeFromFunction<R> + 'static,
T::Target: 'static,
{
type Target = dyn Fn() -> R;
@@ -43,9 +44,9 @@ where
}
}
impl<T: Clone + 'static, R: 'static> Readable for Global<T, R>
impl<T, R> Readable for Global<T, R>
where
T: Readable<Target = R> + InitializeFromFunction<R>,
T: Readable<Target = R> + InitializeFromFunction<R> + Clone + 'static,
{
type Target = R;
type Storage = T::Storage;
@@ -53,23 +54,32 @@ where
#[track_caller]
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
R: 'static,
{
self.resolve().try_read_unchecked()
}
#[track_caller]
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
where
R: 'static,
{
self.resolve().try_peek_unchecked()
}
fn subscribers(&self) -> Option<Subscribers> {
fn subscribers(&self) -> Subscribers
where
R: 'static,
{
self.resolve().subscribers()
}
}
impl<T: Clone + 'static, R: 'static> Writable for Global<T, R>
impl<T: Clone, R> Writable for Global<T, R>
where
T: Writable<Target = R> + InitializeFromFunction<R>,
T: Writable<Target = R> + InitializeFromFunction<R> + 'static,
{
type WriteMetadata = T::WriteMetadata;
@@ -81,9 +91,9 @@ where
}
}
impl<T: Clone + 'static, R: 'static> Global<T, R>
impl<T: Clone, R> Global<T, R>
where
T: Writable<Target = R> + InitializeFromFunction<R>,
T: Writable<Target = R> + InitializeFromFunction<R> + 'static,
{
/// Write this value
pub fn write(&self) -> WritableRef<'static, T, R> {
@@ -93,12 +103,15 @@ where
/// 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 {
pub fn with_mut<O>(&self, f: impl FnOnce(&mut R) -> O) -> O
where
T::Target: 'static,
{
self.resolve().with_mut(f)
}
}
impl<T: Clone + 'static, R> Global<T, R>
impl<T: Clone, R> Global<T, R>
where
T: InitializeFromFunction<R>,
{
@@ -160,7 +173,10 @@ where
/// 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 {
pub fn resolve(&self) -> T
where
T: 'static,
{
let key = self.key();
let context = get_global_context();
@@ -237,7 +253,7 @@ impl From<&'static Location<'static>> for GlobalKey<'static> {
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: GlobalKey) -> Option<Signal<T>> {
pub fn get_signal_with_key<T: 'static>(&self, key: GlobalKey) -> Option<Signal<T>> {
self.map.borrow().get(&key).map(|f| {
*f.downcast_ref::<Signal<T>>().unwrap_or_else(|| {
panic!(
+1 -1
View File
@@ -3,7 +3,7 @@ use crate::read::ReadableExt;
use crate::read_impls;
use crate::Signal;
impl<T> InitializeFromFunction<T> for Signal<T> {
impl<T: 'static> InitializeFromFunction<T> for Signal<T> {
fn initialize_from_function(f: fn() -> T) -> Self {
Signal::new(f())
}
+27 -27
View File
@@ -6,33 +6,33 @@
/// use dioxus::prelude::*;
/// use dioxus_core::Subscribers;
///
/// struct MyCopyValue<T: 'static, S: Storage<T>> {
/// struct MyCopyValue<T, S: 'static> {
/// value: CopyValue<T, S>,
/// }
///
/// impl<T: 'static, S: Storage<T>> MyCopyValue<T, S> {
/// impl<T: 'static, S: Storage<T> + 'static> MyCopyValue<T, S> {
/// fn new_maybe_sync(value: T) -> Self {
/// Self { value: CopyValue::new_maybe_sync(value) }
/// }
/// }
///
/// impl<T: 'static, S: Storage<T>> Readable for MyCopyValue<T, S> {
/// impl<T, S: Storage<T> + 'static> Readable for MyCopyValue<T, S> {
/// type Target = T;
/// type Storage = S;
///
/// fn try_read_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn try_peek_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn subscribers(&self) -> Option<Subscribers> {
/// fn subscribers(&self) -> Subscribers where T: 'static {
/// self.value.subscribers()
/// }
/// }
@@ -79,27 +79,27 @@ macro_rules! default_impl {
/// use dioxus::prelude::*;
/// use dioxus_core::Subscribers;
///
/// struct MyCopyValue<T: 'static, S: Storage<T>> {
/// struct MyCopyValue<T, S: 'static> {
/// value: CopyValue<T, S>,
/// }
///
/// impl<T: 'static, S: Storage<T>> Readable for MyCopyValue<T, S> {
/// impl<T: 'static, S: Storage<T> + 'static> Readable for MyCopyValue<T, S> where T: 'static {
/// type Target = T;
/// type Storage = S;
///
/// fn try_read_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn try_peek_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn subscribers(&self) -> Option<Subscribers> {
/// fn subscribers(&self) -> Subscribers where T: 'static {
/// self.value.subscribers()
/// }
/// }
@@ -157,27 +157,27 @@ macro_rules! read_impls {
/// use dioxus::prelude::*;
/// use dioxus_core::Subscribers;
///
/// struct MyCopyValue<T: 'static, S: Storage<T>> {
/// struct MyCopyValue<T, S: 'static> {
/// value: CopyValue<T, S>,
/// }
///
/// impl<T: 'static, S: Storage<T>> Readable for MyCopyValue<T, S> {
/// impl<T: 'static, S: Storage<T> + 'static> Readable for MyCopyValue<T, S> {
/// type Target = T;
/// type Storage = S;
///
/// fn try_read_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn try_peek_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn subscribers(&self) -> Option<Subscribers> {
/// fn subscribers(&self) -> Subscribers where T: 'static {
/// self.value.subscribers()
/// }
/// }
@@ -238,27 +238,27 @@ macro_rules! fmt_impls {
/// use dioxus::prelude::*;
/// use dioxus_core::Subscribers;
///
/// struct MyCopyValue<T: 'static, S: Storage<T>> {
/// struct MyCopyValue<T, S: 'static> {
/// value: CopyValue<T, S>,
/// }
///
/// impl<T: 'static, S: Storage<T>> Readable for MyCopyValue<T, S> {
/// impl<T: 'static, S: Storage<T> + 'static> Readable for MyCopyValue<T, S> {
/// type Target = T;
/// type Storage = S;
///
/// fn try_read_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn try_peek_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn subscribers(&self) -> Option<Subscribers> {
/// fn subscribers(&self) -> Subscribers where T: 'static {
/// self.value.subscribers()
/// }
/// }
@@ -303,29 +303,29 @@ macro_rules! eq_impls {
/// use generational_box::*;
/// use dioxus::prelude::*;
///
/// struct MyCopyValue<T: 'static, S: Storage<T>> {
/// struct MyCopyValue<T, S: 'static> {
/// value: CopyValue<T, S>,
/// }
///
/// impl<T: 'static, S: Storage<T>> Readable for MyCopyValue<T, S> {
/// impl<T: 'static, S: Storage<T> + 'static> Readable for MyCopyValue<T, S> {
/// type Target = T;
/// type Storage = S;
///
/// fn try_read_unchecked(
/// &self,
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
/// ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> where T: 'static {
/// self.value.try_read_unchecked()
/// }
///
/// fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
/// fn peek_unchecked(&self) -> ReadableRef<'static, Self> where T: 'static {
/// self.value.read_unchecked()
/// }
/// }
///
/// impl<T: 'static, S: Storage<T>> Writable for MyCopyValue<T, S> {
/// impl<T: 'static, S: Storage<T> + 'static> Writable for MyCopyValue<T, S> {
/// fn try_write_unchecked(
/// &self,
/// ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
/// ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> where T: 'static {
/// self.value.try_write_unchecked()
/// }
///
+8 -8
View File
@@ -4,7 +4,7 @@ use generational_box::{AnyStorage, BorrowResult};
use std::ops::Deref;
/// A read only signal that has been mapped to a new type.
pub struct MappedSignal<O: ?Sized + 'static, V: Readable, F = fn(&<V as Readable>::Target) -> &O> {
pub struct MappedSignal<O: ?Sized, V, F = fn(&<V as Readable>::Target) -> &O> {
value: V,
map_fn: F,
_marker: std::marker::PhantomData<O>,
@@ -34,11 +34,9 @@ where
impl<V, O, F> MappedSignal<O, V, F>
where
O: ?Sized,
V: Readable,
F: Fn(&V::Target) -> &O,
{
/// Create a new mapped signal.
pub(crate) fn new(value: V, map_fn: F) -> Self {
pub fn new(value: V, map_fn: F) -> Self {
MappedSignal {
value,
map_fn,
@@ -51,6 +49,7 @@ impl<V, O, F> Readable for MappedSignal<O, V, F>
where
O: ?Sized,
V: Readable,
V::Target: 'static,
F: Fn(&V::Target) -> &O,
{
type Target = O;
@@ -68,15 +67,16 @@ where
Ok(V::Storage::map(value, |v| (self.map_fn)(v)))
}
fn subscribers(&self) -> Option<Subscribers> {
fn subscribers(&self) -> Subscribers {
self.value.subscribers()
}
}
impl<V, O, F> IntoAttributeValue for MappedSignal<O, V, F>
where
O: Clone + IntoAttributeValue,
O: Clone + IntoAttributeValue + 'static,
V: Readable,
V::Target: 'static,
F: Fn(&V::Target) -> &O,
{
fn into_value(self) -> dioxus_core::AttributeValue {
@@ -100,7 +100,7 @@ where
/// Currently only limited to clone types, though could probably specialize for string/arc/rc
impl<V, O, F> Deref for MappedSignal<O, V, F>
where
O: Clone,
O: Clone + 'static,
V: Readable + 'static,
F: Fn(&V::Target) -> &O + 'static,
{
@@ -111,4 +111,4 @@ where
}
}
read_impls!(MappedSignal<T, V, F> where V: Readable, F: Fn(&V::Target) -> &T);
read_impls!(MappedSignal<T, V, F> where V: Readable<Target: 'static>, F: Fn(&V::Target) -> &T);
+25 -14
View File
@@ -9,8 +9,8 @@ use generational_box::{AnyStorage, BorrowResult};
/// A read only signal that has been mapped to a new type.
pub struct MappedMutSignal<
O: ?Sized + 'static,
V: Readable,
O: ?Sized,
V,
F = fn(&<V as Readable>::Target) -> &O,
FMut = fn(&mut <V as Readable>::Target) -> &mut O,
> {
@@ -22,7 +22,7 @@ pub struct MappedMutSignal<
impl<V, O, F, FMut> Clone for MappedMutSignal<O, V, F, FMut>
where
V: Readable + Clone,
V: Clone,
F: Clone,
FMut: Clone,
{
@@ -38,7 +38,7 @@ where
impl<V, O, F, FMut> Copy for MappedMutSignal<O, V, F, FMut>
where
V: Readable + Copy,
V: Copy,
F: Copy,
FMut: Copy,
{
@@ -47,11 +47,9 @@ where
impl<V, O, F, FMut> MappedMutSignal<O, V, F, FMut>
where
O: ?Sized,
V: Readable,
F: Fn(&V::Target) -> &O,
{
/// Create a new mapped signal.
pub(crate) fn new(value: V, map_fn: F, map_fn_mut: FMut) -> Self {
pub fn new(value: V, map_fn: F, map_fn_mut: FMut) -> Self {
MappedMutSignal {
value,
map_fn,
@@ -65,6 +63,7 @@ impl<V, O, F, FMut> Readable for MappedMutSignal<O, V, F, FMut>
where
O: ?Sized,
V: Readable,
V::Target: 'static,
F: Fn(&V::Target) -> &O,
{
type Target = O;
@@ -72,17 +71,26 @@ where
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
O: 'static,
{
let value = self.value.try_read_unchecked()?;
Ok(V::Storage::map(value, |v| (self.map_fn)(v)))
}
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
where
O: 'static,
{
let value = self.value.try_peek_unchecked()?;
Ok(V::Storage::map(value, |v| (self.map_fn)(v)))
}
fn subscribers(&self) -> Option<Subscribers> {
fn subscribers(&self) -> Subscribers
where
O: 'static,
{
self.value.subscribers()
}
}
@@ -91,6 +99,7 @@ impl<V, O, F, FMut> Writable for MappedMutSignal<O, V, F, FMut>
where
O: ?Sized,
V: Writable,
V::Target: 'static,
F: Fn(&V::Target) -> &O,
FMut: Fn(&mut V::Target) -> &mut O,
{
@@ -106,8 +115,9 @@ where
impl<V, O, F, FMut> IntoAttributeValue for MappedMutSignal<O, V, F, FMut>
where
O: Clone + IntoAttributeValue,
O: Clone + IntoAttributeValue + 'static,
V: Readable,
V::Target: 'static,
F: Fn(&V::Target) -> &O,
{
fn into_value(self) -> dioxus_core::AttributeValue {
@@ -134,8 +144,9 @@ where
/// Currently only limited to clone types, though could probably specialize for string/arc/rc
impl<V, O, F, FMut> Deref for MappedMutSignal<O, V, F, FMut>
where
O: Clone,
O: Clone + 'static,
V: Readable + 'static,
V::Target: 'static,
F: Fn(&V::Target) -> &O + 'static,
FMut: 'static,
{
@@ -146,5 +157,5 @@ where
}
}
read_impls!(MappedMutSignal<T, V, F, FMut> where V: Readable, F: Fn(&V::Target) -> &T);
write_impls!(MappedMutSignal<T, V, F, FMut> where V: Writable, F: Fn(&V::Target) -> &T, FMut: Fn(&mut V::Target) -> &mut T);
read_impls!(MappedMutSignal<T, V, F, FMut> where V: Readable<Target: 'static>, F: Fn(&V::Target) -> &T);
write_impls!(MappedMutSignal<T, V, F, FMut> where V: Writable<Target: 'static>, F: Fn(&V::Target) -> &T, FMut: Fn(&mut V::Target) -> &mut T);
+31 -17
View File
@@ -1,4 +1,3 @@
use crate::write::Writable;
use crate::CopyValue;
use crate::{read::Readable, ReadableRef, Signal};
use crate::{read_impls, GlobalMemo, ReadableExt, WritableExt};
@@ -24,17 +23,17 @@ struct UpdateInformation<T> {
#[doc(alias = "Selector")]
#[doc(alias = "UseMemo")]
#[doc(alias = "Memorize")]
pub struct Memo<T: 'static> {
pub struct Memo<T> {
inner: Signal<T>,
update: CopyValue<UpdateInformation<T>>,
}
impl<T: 'static> Memo<T> {
impl<T> Memo<T> {
/// Create a new memo
#[track_caller]
pub fn new(f: impl FnMut() -> T + 'static) -> Self
where
T: PartialEq,
T: PartialEq + 'static,
{
Self::new_with_location(f, std::panic::Location::caller())
}
@@ -45,7 +44,7 @@ impl<T: 'static> Memo<T> {
location: &'static std::panic::Location<'static>,
) -> Self
where
T: PartialEq,
T: PartialEq + 'static,
{
let dirty = Arc::new(AtomicBool::new(false));
let (tx, mut rx) = futures_channel::mpsc::unbounded();
@@ -114,7 +113,7 @@ impl<T: 'static> Memo<T> {
#[track_caller]
pub const fn global(constructor: fn() -> T) -> GlobalMemo<T>
where
T: PartialEq,
T: PartialEq + 'static,
{
GlobalMemo::new(constructor)
}
@@ -123,7 +122,7 @@ impl<T: 'static> Memo<T> {
#[tracing::instrument(skip(self))]
fn recompute(&self)
where
T: PartialEq,
T: PartialEq + 'static,
{
let mut update_copy = self.update;
let update_write = update_copy.write();
@@ -141,12 +140,18 @@ impl<T: 'static> Memo<T> {
}
/// Get the scope that the signal was created in.
pub fn origin_scope(&self) -> ScopeId {
pub fn origin_scope(&self) -> ScopeId
where
T: 'static,
{
self.inner.origin_scope()
}
/// Get the id of the signal.
pub fn id(&self) -> generational_box::GenerationalBoxId {
pub fn id(&self) -> generational_box::GenerationalBoxId
where
T: 'static,
{
self.inner.id()
}
}
@@ -161,7 +166,10 @@ where
#[track_caller]
fn try_read_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError> {
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
T: 'static,
{
// Read the inner generational box instead of the signal so we have more fine grained control over exactly when the subscription happens
let read = self.inner.inner.try_read_unchecked()?;
@@ -192,18 +200,24 @@ where
///
/// If the signal has been dropped, this will panic.
#[track_caller]
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
where
T: 'static,
{
self.inner.try_peek_unchecked()
}
fn subscribers(&self) -> Option<Subscribers> {
fn subscribers(&self) -> Subscribers
where
T: 'static,
{
self.inner.subscribers()
}
}
impl<T> IntoAttributeValue for Memo<T>
where
T: Clone + IntoAttributeValue + PartialEq,
T: Clone + IntoAttributeValue + PartialEq + 'static,
{
fn into_value(self) -> dioxus_core::AttributeValue {
self.with(|f| f.clone().into_value())
@@ -212,7 +226,7 @@ where
impl<T> IntoDynNode for Memo<T>
where
T: Clone + IntoDynNode + PartialEq,
T: Clone + IntoDynNode + PartialEq + 'static,
{
fn into_dyn_node(self) -> dioxus_core::DynamicNode {
self().into_dyn_node()
@@ -227,7 +241,7 @@ impl<T: 'static> PartialEq for Memo<T> {
impl<T: Clone> Deref for Memo<T>
where
T: PartialEq,
T: PartialEq + 'static,
{
type Target = dyn Fn() -> T;
@@ -238,10 +252,10 @@ where
read_impls!(Memo<T> where T: PartialEq);
impl<T: 'static> Clone for Memo<T> {
impl<T> Clone for Memo<T> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static> Copy for Memo<T> {}
impl<T> Copy for Memo<T> {}
+1
View File
@@ -7,6 +7,7 @@ pub struct ReadFromMarker<M>(std::marker::PhantomData<M>);
impl<T, O, M> SuperFrom<T, ReadFromMarker<M>> for ReadSignal<O>
where
O: SuperFrom<T, M> + 'static,
T: 'static,
{
fn super_from(input: T) -> Self {
ReadSignal::new(Signal::new(O::super_from(input)))
+91 -50
View File
@@ -37,7 +37,7 @@ pub type ReadableRef<'a, T: Readable, O = <T as Readable>::Target> =
/// ```
pub trait Readable {
/// The target type of the reference.
type Target: ?Sized + 'static;
type Target: ?Sized;
/// The type of the storage this readable uses.
type Storage: AnyStorage;
@@ -47,7 +47,9 @@ pub trait Readable {
/// 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>;
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
Self::Target: 'static;
/// Try to peek the current value of the signal without subscribing to updates. If the value has
/// been dropped, this will return an error.
@@ -55,10 +57,14 @@ pub trait Readable {
/// NOTE: This method is completely safe because borrow checking is done at runtime.
fn try_peek_unchecked(
&self,
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>;
) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
where
Self::Target: 'static;
/// Get the underlying subscriber list for this readable. This is used to track when the value changes and notify subscribers.
fn subscribers(&self) -> Option<Subscribers>;
fn subscribers(&self) -> Subscribers
where
Self::Target: 'static;
}
/// An extension trait for `Readable` types that provides some convenience methods.
@@ -67,13 +73,19 @@ pub trait ReadableExt: Readable {
/// If the value has been dropped, this will panic. Calling this on a Signal is the same as
/// using the signal() syntax to read and subscribe to its value
#[track_caller]
fn read(&self) -> ReadableRef<Self> {
fn read(&self) -> ReadableRef<Self>
where
Self::Target: 'static,
{
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.
#[track_caller]
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
fn try_read(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError>
where
Self::Target: 'static,
{
self.try_read_unchecked()
.map(Self::Storage::downcast_lifetime_ref)
}
@@ -82,7 +94,10 @@ pub trait ReadableExt: Readable {
///
/// NOTE: This method is completely safe because borrow checking is done at runtime.
#[track_caller]
fn read_unchecked(&self) -> ReadableRef<'static, Self> {
fn read_unchecked(&self) -> ReadableRef<'static, Self>
where
Self::Target: 'static,
{
self.try_read_unchecked().unwrap()
}
@@ -121,14 +136,20 @@ pub trait ReadableExt: Readable {
/// }
/// ```
#[track_caller]
fn peek(&self) -> ReadableRef<Self> {
fn peek(&self) -> ReadableRef<Self>
where
Self::Target: 'static,
{
Self::Storage::downcast_lifetime_ref(self.peek_unchecked())
}
/// Try to peek the current value of the signal without subscribing to updates. If the value has
/// been dropped, this will return an error.
#[track_caller]
fn try_peek(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError> {
fn try_peek(&self) -> Result<ReadableRef<Self>, generational_box::BorrowError>
where
Self::Target: 'static,
{
self.try_peek_unchecked()
.map(Self::Storage::downcast_lifetime_ref)
}
@@ -137,13 +158,19 @@ pub trait ReadableExt: Readable {
///
/// If the signal has been dropped, this will panic.
#[track_caller]
fn peek_unchecked(&self) -> ReadableRef<'static, Self> {
fn peek_unchecked(&self) -> ReadableRef<'static, Self>
where
Self::Target: 'static,
{
self.try_peek_unchecked().unwrap()
}
/// Map the readable type to a new type. This lets you provide a view into a readable type without needing to clone the inner value.
/// Map the references of the readable value to a new type. This lets you provide a view
/// into the readable value without creating a new signal or cloning the value.
///
/// Anything that subscribes to the readable value will be rerun whenever the original value changes, even if the view does not change. If you want to memorize the view, you can use a [`crate::Memo`] instead.
/// Anything that subscribes to the readable value will be rerun whenever the original value changes, even if the view does not
/// change. If you want to memorize the view, you can use a [`crate::Memo`] instead. For fine grained scoped updates, use
/// stores instead
///
/// # Example
/// ```rust
@@ -167,8 +194,8 @@ pub trait ReadableExt: Readable {
/// ```
fn map<F, O>(self, f: F) -> MappedSignal<O, Self, F>
where
Self: Clone + Sized + 'static,
F: Fn(&Self::Target) -> &O + 'static,
Self: Clone + Sized,
F: Fn(&Self::Target) -> &O,
{
MappedSignal::new(self, f)
}
@@ -177,20 +204,26 @@ pub trait ReadableExt: Readable {
#[track_caller]
fn cloned(&self) -> Self::Target
where
Self::Target: Clone,
Self::Target: Clone + 'static,
{
self.read().clone()
}
/// Run a function with a reference to the value. If the value has been dropped, this will panic.
#[track_caller]
fn with<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O {
fn with<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
where
Self::Target: 'static,
{
f(&*self.read())
}
/// Run a function with a reference to the value. If the value has been dropped, this will panic.
#[track_caller]
fn with_peek<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O {
fn with_peek<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
where
Self::Target: 'static,
{
f(&*self.peek())
}
@@ -198,7 +231,7 @@ pub trait ReadableExt: Readable {
#[track_caller]
fn index<I>(&self, index: I) -> ReadableRef<Self, <Self::Target as std::ops::Index<I>>::Output>
where
Self::Target: std::ops::Index<I>,
Self::Target: std::ops::Index<I> + 'static,
{
<Self::Storage as AnyStorage>::map(self.read(), |v| v.index(index))
}
@@ -210,7 +243,7 @@ pub trait ReadableExt: Readable {
unsafe fn deref_impl<'a>(&self) -> &'a dyn Fn() -> Self::Target
where
Self: Sized + 'a,
Self::Target: Clone,
Self::Target: Clone + 'static,
{
// https://github.com/dtolnay/case-studies/tree/master/callable-types
@@ -256,36 +289,52 @@ pub trait ReadableBoxExt: Readable<Storage = UnsyncStorage> {
ReadSignal::new(self)
}
}
impl<R: Readable<Storage = UnsyncStorage> + ?Sized> ReadableBoxExt for R {}
/// An extension trait for `Readable<Vec<T>>` that provides some convenience methods.
pub trait ReadableVecExt<T: 'static>: Readable<Target = Vec<T>> {
pub trait ReadableVecExt<T>: Readable<Target = Vec<T>> {
/// Returns the length of the inner vector.
#[track_caller]
fn len(&self) -> usize {
fn len(&self) -> usize
where
T: 'static,
{
self.with(|v| v.len())
}
/// Returns true if the inner vector is empty.
#[track_caller]
fn is_empty(&self) -> bool {
fn is_empty(&self) -> bool
where
T: 'static,
{
self.with(|v| v.is_empty())
}
/// Get the first element of the inner vector.
#[track_caller]
fn first(&self) -> Option<ReadableRef<Self, T>> {
fn first(&self) -> Option<ReadableRef<Self, T>>
where
T: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.first())
}
/// Get the last element of the inner vector.
#[track_caller]
fn last(&self) -> Option<ReadableRef<Self, T>> {
fn last(&self) -> Option<ReadableRef<Self, T>>
where
T: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.last())
}
/// Get the element at the given index of the inner vector.
#[track_caller]
fn get(&self, index: usize) -> Option<ReadableRef<Self, T>> {
fn get(&self, index: usize) -> Option<ReadableRef<Self, T>>
where
T: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.get(index))
}
@@ -318,45 +367,39 @@ impl<'a, T: 'static, R: Readable<Target = Vec<T>>> Iterator for ReadableValueIte
}
}
impl<T, R> ReadableVecExt<T> for R
where
T: 'static,
R: Readable<Target = Vec<T>>,
{
}
impl<T, R> ReadableVecExt<T> for R where R: Readable<Target = Vec<T>> {}
/// An extension trait for `Readable<Option<T>>` that provides some convenience methods.
pub trait ReadableOptionExt<T: 'static>: Readable<Target = Option<T>> {
pub trait ReadableOptionExt<T>: Readable<Target = Option<T>> {
/// Unwraps the inner value and clones it.
#[track_caller]
fn unwrap(&self) -> T
where
T: Clone,
T: Clone + 'static,
{
self.as_ref().unwrap().clone()
}
/// Attempts to read the inner value of the Option.
#[track_caller]
fn as_ref(&self) -> Option<ReadableRef<Self, T>> {
fn as_ref(&self) -> Option<ReadableRef<Self, T>>
where
T: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref())
}
}
impl<T, R> ReadableOptionExt<T> for R
where
T: 'static,
R: Readable<Target = Option<T>>,
{
}
impl<T, R> ReadableOptionExt<T> for R where R: Readable<Target = Option<T>> {}
/// An extension trait for `Readable<Option<T>>` that provides some convenience methods.
pub trait ReadableResultExt<T: 'static, E: 'static>: Readable<Target = Result<T, E>> {
pub trait ReadableResultExt<T, E>: Readable<Target = Result<T, E>> {
/// Unwraps the inner value and clones it.
#[track_caller]
fn unwrap(&self) -> T
where
T: Clone,
T: Clone + 'static,
E: 'static,
{
self.as_ref()
.unwrap_or_else(|_| panic!("Tried to unwrap a Result that was an error"))
@@ -365,17 +408,15 @@ pub trait ReadableResultExt<T: 'static, E: 'static>: Readable<Target = Result<T,
/// Attempts to read the inner value of the Option.
#[track_caller]
fn as_ref(&self) -> Result<ReadableRef<Self, T>, ReadableRef<Self, E>> {
fn as_ref(&self) -> Result<ReadableRef<Self, T>, ReadableRef<Self, E>>
where
T: 'static,
E: 'static,
{
<Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref().ok()).ok_or(
<Self::Storage as AnyStorage>::map(self.read(), |v| v.as_ref().err().unwrap()),
)
}
}
impl<T, E, R> ReadableResultExt<T, E> for R
where
T: 'static,
E: 'static,
R: Readable<Target = Result<T, E>>,
{
}
impl<T, E, R> ReadableResultExt<T, E> for R where R: Readable<Target = Result<T, E>> {}
+8 -9
View File
@@ -1,4 +1,4 @@
use crate::{write::Writable, ReadableExt};
use crate::{write::WritableExt, ReadableExt};
use std::hash::Hash;
use dioxus_core::ReactiveContext;
@@ -9,12 +9,11 @@ use crate::{CopyValue, ReadSignal, Signal, SignalData};
use rustc_hash::FxHashMap;
/// An object that can efficiently compare a value to a set of values.
#[derive(Debug)]
pub struct SetCompare<R: 'static, S: Storage<SignalData<bool>> = UnsyncStorage> {
pub struct SetCompare<R, S: 'static = UnsyncStorage> {
subscribers: CopyValue<FxHashMap<R, Signal<bool, S>>>,
}
impl<R: Eq + Hash> SetCompare<R> {
impl<R: Eq + Hash + 'static> SetCompare<R> {
/// Creates a new [`SetCompare`] which efficiently tracks when a value changes to check if it is equal to a set of values.
///
/// Generally, you shouldn't need to use this hook. Instead you can use [`crate::Memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
@@ -24,13 +23,13 @@ impl<R: Eq + Hash> SetCompare<R> {
}
}
impl<R: Eq + Hash, S: Storage<SignalData<bool>>> SetCompare<R, S> {
impl<R: Eq + Hash + 'static, S: Storage<SignalData<bool>> + 'static> SetCompare<R, S> {
/// Creates a new [`SetCompare`] that may be `Sync + Send` which efficiently tracks when a value changes to check if it is equal to a set of values.
///
/// Generally, you shouldn't need to use this hook. Instead you can use [`crate::Memo`]. If you have many values that you need to compare to a single value, this hook will change updates from O(n) to O(1) where n is the number of values you are comparing to.
#[track_caller]
pub fn new_maybe_sync(mut f: impl FnMut() -> R + 'static) -> SetCompare<R> {
let subscribers: CopyValue<FxHashMap<R, Signal<bool>>> =
pub fn new_maybe_sync(mut f: impl FnMut() -> R + 'static) -> SetCompare<R, S> {
let subscribers: CopyValue<FxHashMap<R, Signal<bool, S>>> =
CopyValue::new(FxHashMap::default());
let mut previous = CopyValue::new(None);
@@ -67,7 +66,7 @@ impl<R: Eq + Hash, S: Storage<SignalData<bool>>> SetCompare<R, S> {
}
}
impl<R: Eq + Hash> SetCompare<R> {
impl<R: Eq + Hash + 'static> SetCompare<R> {
/// Returns a signal which is true when the value is equal to the value passed to this function.
pub fn equal(&mut self, value: R) -> ReadSignal<bool> {
let subscribers = self.subscribers.write();
@@ -85,7 +84,7 @@ impl<R: Eq + Hash> SetCompare<R> {
}
}
impl<R: 'static, S: Storage<SignalData<bool>>> PartialEq for SetCompare<R, S> {
impl<R, S: Storage<SignalData<bool>>> PartialEq for SetCompare<R, S> {
fn eq(&self, other: &Self) -> bool {
self.subscribers == other.subscribers
}
+57 -28
View File
@@ -4,13 +4,13 @@ use crate::{
};
use dioxus_core::{IntoAttributeValue, IntoDynNode, ReactiveContext, ScopeId, Subscribers};
use generational_box::{BorrowResult, Storage, SyncStorage, UnsyncStorage};
use std::{any::Any, collections::HashSet, ops::Deref, sync::Arc, sync::Mutex};
use std::{collections::HashSet, ops::Deref, sync::Arc, sync::Mutex};
#[doc = include_str!("../docs/signals.md")]
#[doc(alias = "State")]
#[doc(alias = "UseState")]
#[doc(alias = "UseRef")]
pub struct Signal<T: 'static, S: Storage<SignalData<T>> = UnsyncStorage> {
pub struct Signal<T, S: 'static = UnsyncStorage> {
pub(crate) inner: CopyValue<SignalData<T>, S>,
}
@@ -139,11 +139,14 @@ impl<T: PartialEq + 'static> Signal<T> {
}
}
impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
impl<T, S: Storage<SignalData<T>>> Signal<T, S> {
/// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
#[track_caller]
#[tracing::instrument(skip(value))]
pub fn new_maybe_sync(value: T) -> Self {
pub fn new_maybe_sync(value: T) -> Self
where
T: 'static,
{
Self {
inner: CopyValue::<SignalData<T>, S>::new_maybe_sync(SignalData {
subscribers: Default::default(),
@@ -165,7 +168,10 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
/// use_hook(move || Signal::new_with_caller(function(), caller))
/// }
/// ```
pub fn new_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
pub fn new_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self
where
T: 'static,
{
Self {
inner: CopyValue::new_with_caller(
SignalData {
@@ -178,7 +184,10 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
}
/// Create a new Signal without an owner. This will leak memory if you don't manually drop it.
pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self
where
T: 'static,
{
Self {
inner: CopyValue::leak_with_caller(
SignalData {
@@ -217,7 +226,10 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
}
/// Point to another signal. This will subscribe the other signal to all subscribers of this signal.
pub fn point_to(&self, other: Self) -> BorrowResult {
pub fn point_to(&self, other: Self) -> BorrowResult
where
T: 'static,
{
#[allow(clippy::mutable_key_type)]
let this_subscribers = self.inner.value.read().subscribers.lock().unwrap().clone();
let other_read = other.inner.value.read();
@@ -228,7 +240,10 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
}
/// Drop the value out of the signal, invalidating the signal in the process.
pub fn manually_drop(&self) {
pub fn manually_drop(&self)
where
T: 'static,
{
self.inner.manually_drop()
}
@@ -237,7 +252,10 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
self.inner.origin_scope()
}
fn update_subscribers(&self) {
fn update_subscribers(&self)
where
T: 'static,
{
{
let inner = self.inner.read();
@@ -283,7 +301,7 @@ impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
/// *signal.write_silent() += 1;
/// ```
///
/// Instead you can use the [`peek`](ReadableExt::peek) and [`write`](Signal::write) methods instead. The peek method will not subscribe to the current scope which will avoid an infinite loop if you are reading and writing to the same signal in the same scope.
/// Instead you can use the [`peek`](ReadableExt::peek) and [`write`](WritableExt::write) methods instead. The peek method will not subscribe to the current scope which will avoid an infinite loop if you are reading and writing to the same signal in the same scope.
/// ```rust, no_run
/// # use dioxus::prelude::*;
/// let mut signal = use_signal(|| 0);
@@ -384,7 +402,10 @@ impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
type Storage = S;
#[track_caller]
fn try_read_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
fn try_read_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
where
T: 'static,
{
let inner = self.inner.try_read_unchecked()?;
if let Some(reactive_context) = ReactiveContext::current() {
@@ -399,19 +420,25 @@ impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
///
/// If the signal has been dropped, this will panic.
#[track_caller]
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>>
where
T: 'static,
{
self.inner
.try_read_unchecked()
.map(|inner| S::map(inner, |v| &v.value))
}
fn subscribers(&self) -> Option<Subscribers> {
Some(self.inner.read().subscribers.clone().into())
fn subscribers(&self) -> Subscribers
where
T: 'static,
{
self.inner.read().subscribers.clone().into()
}
}
impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
type WriteMetadata = Box<dyn Any>;
type WriteMetadata = SignalSubscriberDrop<T, S>;
#[track_caller]
fn try_write_unchecked(
@@ -423,11 +450,11 @@ impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
let borrow = S::map_mut(inner.into_inner(), |v| &mut v.value);
WriteLock::new_with_metadata(
borrow,
Box::new(SignalSubscriberDrop {
SignalSubscriberDrop {
signal: *self,
#[cfg(debug_assertions)]
origin,
}) as Box<dyn Any>,
},
)
})
}
@@ -435,7 +462,7 @@ impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
impl<T> IntoAttributeValue for Signal<T>
where
T: Clone + IntoAttributeValue,
T: Clone + IntoAttributeValue + 'static,
{
fn into_value(self) -> dioxus_core::AttributeValue {
self.with(|f| f.clone().into_value())
@@ -444,25 +471,25 @@ where
impl<T> IntoDynNode for Signal<T>
where
T: Clone + IntoDynNode,
T: Clone + IntoDynNode + 'static,
{
fn into_dyn_node(self) -> dioxus_core::DynamicNode {
self().into_dyn_node()
}
}
impl<T: 'static, S: Storage<SignalData<T>>> PartialEq for Signal<T, S> {
impl<T, S: Storage<SignalData<T>>> PartialEq for Signal<T, S> {
fn eq(&self, other: &Self) -> bool {
self.inner == other.inner
}
}
impl<T: 'static, S: Storage<SignalData<T>>> Eq for Signal<T, S> {}
impl<T, S: Storage<SignalData<T>>> Eq for Signal<T, S> {}
/// Allow calling a signal with signal() syntax
///
/// Currently only limited to copy types, though could probably specialize for string/arc/rc
impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for Signal<T, S> {
impl<T: Clone + 'static, S: Storage<SignalData<T>> + 'static> Deref for Signal<T, S> {
type Target = dyn Fn() -> T;
fn deref(&self) -> &Self::Target {
@@ -471,7 +498,7 @@ impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for Signal<T, S> {
}
#[cfg(feature = "serialize")]
impl<T: serde::Serialize + 'static, Store: Storage<SignalData<T>>> serde::Serialize
impl<T: serde::Serialize + 'static, Store: Storage<SignalData<T>> + 'static> serde::Serialize
for Signal<T, Store>
{
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
@@ -480,7 +507,7 @@ impl<T: serde::Serialize + 'static, Store: Storage<SignalData<T>>> serde::Serial
}
#[cfg(feature = "serialize")]
impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>> + 'static>
serde::Deserialize<'de> for Signal<T, Store>
{
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
@@ -488,14 +515,16 @@ impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
}
}
struct SignalSubscriberDrop<T: 'static, S: Storage<SignalData<T>>> {
#[doc(hidden)]
/// A drop guard that will update the subscribers of the signal when it is dropped.
pub struct SignalSubscriberDrop<T: 'static, S: Storage<SignalData<T>> + 'static> {
signal: Signal<T, S>,
#[cfg(debug_assertions)]
origin: &'static std::panic::Location<'static>,
}
#[allow(clippy::no_effect)]
impl<T: 'static, S: Storage<SignalData<T>>> Drop for SignalSubscriberDrop<T, S> {
impl<T: 'static, S: Storage<SignalData<T>> + 'static> Drop for SignalSubscriberDrop<T, S> {
fn drop(&mut self) {
#[cfg(debug_assertions)]
{
@@ -512,10 +541,10 @@ fmt_impls!(Signal<T, S: Storage<SignalData<T>>>);
default_impl!(Signal<T, S: Storage<SignalData<T>>>);
write_impls!(Signal<T, S: Storage<SignalData<T>>>);
impl<T: 'static, S: Storage<SignalData<T>>> Clone for Signal<T, S> {
impl<T, S> Clone for Signal<T, S> {
fn clone(&self) -> Self {
*self
}
}
impl<T: 'static, S: Storage<SignalData<T>>> Copy for Signal<T, S> {}
impl<T, S> Copy for Signal<T, S> {}
+1 -1
View File
@@ -4,7 +4,7 @@ use warnings::warning;
/// A warning that is triggered when a copy value is used in a higher scope that it is owned by
#[warning]
pub fn copy_value_hoisted<T: 'static, S: generational_box::Storage<T>>(
pub fn copy_value_hoisted<T: 'static, S: generational_box::Storage<T> + 'static>(
value: &crate::CopyValue<T, S>,
caller: &'static std::panic::Location<'static>,
) {
+114 -65
View File
@@ -36,34 +36,16 @@ pub type WritableRef<'a, T: Writable, O = <T as Readable>::Target> =
/// ```
pub trait Writable: Readable {
/// Additional data associated with the write reference.
type WriteMetadata: 'static;
/// Get a mutable reference to the value. If the value has been dropped, this will panic.
#[track_caller]
fn write(&mut self) -> WritableRef<'_, Self> {
self.try_write().unwrap()
}
/// 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(WriteLock::downcast_lifetime)
}
type WriteMetadata;
/// Try to get a mutable reference to the value without checking the lifetime. This will update any subscribers.
///
/// 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>;
/// Get a mutable reference to the value without checking the lifetime. This will update any subscribers.
///
/// NOTE: This method is completely safe because borrow checking is done at runtime.
#[track_caller]
fn write_unchecked(&self) -> WritableRef<'static, Self> {
self.try_write_unchecked().unwrap()
}
) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError>
where
Self::Target: 'static;
}
/// A mutable reference to a writable value. This reference acts similarly to [`std::cell::RefMut`], but it has extra debug information
@@ -176,7 +158,7 @@ pub trait Writable: Readable {
/// - 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.
/// - D is the additional data associated with the write reference. This is used by signals to track when the write is dropped
pub struct WriteLock<'a, T: ?Sized + 'static, S: AnyStorage = UnsyncStorage, D = ()> {
pub struct WriteLock<'a, T: ?Sized + 'a, S: AnyStorage = UnsyncStorage, D = ()> {
write: S::Mut<'a, T>,
data: D,
}
@@ -256,7 +238,7 @@ impl<'a, T: ?Sized, S: AnyStorage, D> WriteLock<'a, T, S, D> {
impl<T, S, D> Deref for WriteLock<'_, T, S, D>
where
S: AnyStorage,
T: ?Sized + 'static,
T: ?Sized,
{
type Target = T;
@@ -268,7 +250,7 @@ where
impl<T, S, D> DerefMut for WriteLock<'_, T, S, D>
where
S: AnyStorage,
T: ?Sized + 'static,
T: ?Sized,
{
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.write
@@ -277,9 +259,41 @@ where
/// An extension trait for [`Writable`] that provides some convenience methods.
pub trait WritableExt: Writable {
/// Map the readable type to a new type. This lets you provide a view into a readable type without needing to clone the inner value.
/// Get a mutable reference to the value. If the value has been dropped, this will panic.
#[track_caller]
fn write(&mut self) -> WritableRef<'_, Self>
where
Self::Target: 'static,
{
self.try_write().unwrap()
}
/// Try to get a mutable reference to the value.
#[track_caller]
fn try_write(&mut self) -> Result<WritableRef<'_, Self>, generational_box::BorrowMutError>
where
Self::Target: 'static,
{
self.try_write_unchecked().map(WriteLock::downcast_lifetime)
}
/// Get a mutable reference to the value without checking the lifetime. This will update any subscribers.
///
/// Anything that subscribes to the readable value will be rerun whenever the original value changes, even if the view does not change. If you want to memorize the view, you can use a [`crate::Memo`] instead.
/// NOTE: This method is completely safe because borrow checking is done at runtime.
#[track_caller]
fn write_unchecked(&self) -> WritableRef<'static, Self>
where
Self::Target: 'static,
{
self.try_write_unchecked().unwrap()
}
/// Map the references and mutable references of the writable value to a new type. This lets you provide a view
/// into the writable value without creating a new signal or cloning the value.
///
/// Anything that subscribes to the writable value will be rerun whenever the original value changes or you write to this
/// scoped value, even if the view does not change. If you want to memorize the view, you can use a [`crate::Memo`] instead.
/// For fine grained scoped updates, use stores instead
///
/// # Example
/// ```rust
@@ -306,17 +320,20 @@ pub trait WritableExt: Writable {
/// ```
fn map_mut<O, F, FMut>(self, f: F, f_mut: FMut) -> MappedMutSignal<O, Self, F, FMut>
where
Self: Clone + Sized + 'static,
O: ?Sized + 'static,
F: Fn(&Self::Target) -> &O + 'static,
FMut: Fn(&mut Self::Target) -> &mut O + 'static,
Self: Sized,
O: ?Sized,
F: Fn(&Self::Target) -> &O,
FMut: Fn(&mut Self::Target) -> &mut O,
{
MappedMutSignal::new(self, f, f_mut)
}
/// Run a function with a mutable reference to the value. If the value has been dropped, this will panic.
#[track_caller]
fn with_mut<O>(&mut self, f: impl FnOnce(&mut Self::Target) -> O) -> O {
fn with_mut<O>(&mut self, f: impl FnOnce(&mut Self::Target) -> O) -> O
where
Self::Target: 'static,
{
f(&mut *self.write())
}
@@ -324,7 +341,7 @@ pub trait WritableExt: Writable {
#[track_caller]
fn set(&mut self, value: Self::Target)
where
Self::Target: Sized,
Self::Target: Sized + 'static,
{
*self.write() = value;
}
@@ -333,7 +350,7 @@ pub trait WritableExt: Writable {
#[track_caller]
fn toggle(&mut self)
where
Self::Target: std::ops::Not<Output = Self::Target> + Clone,
Self::Target: std::ops::Not<Output = Self::Target> + Clone + 'static,
{
let inverted = !(*self.peek()).clone();
self.set(inverted);
@@ -346,7 +363,7 @@ pub trait WritableExt: Writable {
index: I,
) -> WritableRef<'_, Self, <Self::Target as std::ops::Index<I>>::Output>
where
Self::Target: std::ops::IndexMut<I>,
Self::Target: std::ops::IndexMut<I> + 'static,
{
WriteLock::map(self.write(), |v| v.index_mut(index))
}
@@ -355,7 +372,7 @@ pub trait WritableExt: Writable {
#[track_caller]
fn take(&mut self) -> Self::Target
where
Self::Target: Default,
Self::Target: Default + 'static,
{
self.with_mut(std::mem::take)
}
@@ -364,7 +381,7 @@ pub trait WritableExt: Writable {
#[track_caller]
fn replace(&mut self, value: Self::Target) -> Self::Target
where
Self::Target: Sized,
Self::Target: Sized + 'static,
{
self.with_mut(|v| std::mem::replace(v, value))
}
@@ -390,16 +407,22 @@ impl<T: Writable<Storage = UnsyncStorage> + 'static> WritableBoxedExt for T {
}
/// An extension trait for [`Writable<Option<T>>`]` that provides some convenience methods.
pub trait WritableOptionExt<T: 'static>: Writable<Target = Option<T>> {
pub trait WritableOptionExt<T>: Writable<Target = Option<T>> {
/// Gets the value out of the Option, or inserts the given value if the Option is empty.
#[track_caller]
fn get_or_insert(&mut self, default: T) -> WritableRef<'_, Self, T> {
fn get_or_insert(&mut self, default: T) -> WritableRef<'_, Self, T>
where
T: 'static,
{
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.
#[track_caller]
fn get_or_insert_with(&mut self, default: impl FnOnce() -> T) -> WritableRef<'_, Self, T> {
fn get_or_insert_with(&mut self, default: impl FnOnce() -> T) -> WritableRef<'_, Self, T>
where
T: 'static,
{
let is_none = self.read().is_none();
if is_none {
self.with_mut(|v| *v = Some(default()));
@@ -411,83 +434,114 @@ 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<WritableRef<'_, Self, T>> {
fn as_mut(&mut self) -> Option<WritableRef<'_, Self, T>>
where
T: 'static,
{
WriteLock::filter_map(self.write(), |v: &mut Option<T>| v.as_mut())
}
}
impl<T, W> WritableOptionExt<T> for W
where
T: 'static,
W: Writable<Target = Option<T>>,
{
}
impl<T, W> WritableOptionExt<T> for W where W: Writable<Target = Option<T>> {}
/// An extension trait for [`Writable<Vec<T>>`] that provides some convenience methods.
pub trait WritableVecExt<T: 'static>: Writable<Target = Vec<T>> {
pub trait WritableVecExt<T>: Writable<Target = Vec<T>> {
/// Pushes a new value to the end of the vector.
#[track_caller]
fn push(&mut self, value: T) {
fn push(&mut self, value: T)
where
T: 'static,
{
self.with_mut(|v| v.push(value))
}
/// Pops the last value from the vector.
#[track_caller]
fn pop(&mut self) -> Option<T> {
fn pop(&mut self) -> Option<T>
where
T: 'static,
{
self.with_mut(|v| v.pop())
}
/// Inserts a new value at the given index.
#[track_caller]
fn insert(&mut self, index: usize, value: T) {
fn insert(&mut self, index: usize, value: T)
where
T: 'static,
{
self.with_mut(|v| v.insert(index, value))
}
/// Removes the value at the given index.
#[track_caller]
fn remove(&mut self, index: usize) -> T {
fn remove(&mut self, index: usize) -> T
where
T: 'static,
{
self.with_mut(|v| v.remove(index))
}
/// Clears the vector, removing all values.
#[track_caller]
fn clear(&mut self) {
fn clear(&mut self)
where
T: 'static,
{
self.with_mut(|v| v.clear())
}
/// Extends the vector with the given iterator.
#[track_caller]
fn extend(&mut self, iter: impl IntoIterator<Item = T>) {
fn extend(&mut self, iter: impl IntoIterator<Item = T>)
where
T: 'static,
{
self.with_mut(|v| v.extend(iter))
}
/// Truncates the vector to the given length.
#[track_caller]
fn truncate(&mut self, len: usize) {
fn truncate(&mut self, len: usize)
where
T: 'static,
{
self.with_mut(|v| v.truncate(len))
}
/// Swaps two values in the vector.
#[track_caller]
fn swap_remove(&mut self, index: usize) -> T {
fn swap_remove(&mut self, index: usize) -> T
where
T: 'static,
{
self.with_mut(|v| v.swap_remove(index))
}
/// Retains only the values that match the given predicate.
#[track_caller]
fn retain(&mut self, f: impl FnMut(&T) -> bool) {
fn retain(&mut self, f: impl FnMut(&T) -> bool)
where
T: 'static,
{
self.with_mut(|v| v.retain(f))
}
/// Splits the vector into two at the given index.
#[track_caller]
fn split_off(&mut self, at: usize) -> Vec<T> {
fn split_off(&mut self, at: usize) -> Vec<T>
where
T: 'static,
{
self.with_mut(|v| v.split_off(at))
}
/// Try to mutably get an element from the vector.
#[track_caller]
fn get_mut(&mut self, index: usize) -> Option<WritableRef<'_, Self, T>> {
fn get_mut(&mut self, index: usize) -> Option<WritableRef<'_, Self, T>>
where
T: 'static,
{
WriteLock::filter_map(self.write(), |v: &mut Vec<T>| v.get_mut(index))
}
@@ -524,9 +578,4 @@ impl<'a, T: 'static, R: Writable<Target = Vec<T>>> Iterator for WritableValueIte
}
}
impl<T, W> WritableVecExt<T> for W
where
T: 'static,
W: Writable<Target = Vec<T>>,
{
}
impl<T, W> WritableVecExt<T> for W where W: Writable<Target = Vec<T>> {}