dioxus_signals/
read.rs

1use std::{mem::MaybeUninit, ops::Index};
2
3use dioxus_core::Subscribers;
4use generational_box::{AnyStorage, UnsyncStorage};
5
6use crate::{MappedSignal, ReadSignal};
7
8/// A reference to a value that can be read from.
9#[allow(type_alias_bounds)]
10pub type ReadableRef<'a, T: Readable, O = <T as Readable>::Target> =
11    <T::Storage as AnyStorage>::Ref<'a, O>;
12
13/// A trait for states that can be read from like [`crate::Signal`], [`crate::GlobalSignal`], or [`crate::ReadSignal`]. You may choose to accept this trait as a parameter instead of the concrete type to allow for more flexibility in your API. For example, instead of creating two functions, one that accepts a [`crate::Signal`] and one that accepts a [`crate::GlobalSignal`], you can create one function that accepts a [`Readable`] type.
14///
15/// # Example
16/// ```rust
17/// # use dioxus::prelude::*;
18/// fn double(something_readable: &impl Readable<Target = i32>) -> i32 {
19///     something_readable.cloned() * 2
20/// }
21///
22/// static COUNT: GlobalSignal<i32> = Signal::global(|| 0);
23///
24/// fn MyComponent(count: Signal<i32>) -> Element {
25///     // Since we defined the function in terms of the readable trait, we can use it with any readable type (Signal, GlobalSignal, ReadSignal, etc)
26///     let doubled = use_memo(move || double(&count));
27///     let global_count_doubled = use_memo(|| double(&COUNT));
28///     rsx! {
29///         div {
30///             "Count local: {count}"
31///             "Doubled local: {doubled}"
32///             "Count global: {COUNT}"
33///             "Doubled global: {global_count_doubled}"
34///         }
35///     }
36/// }
37/// ```
38pub trait Readable {
39    /// The target type of the reference.
40    type Target: ?Sized;
41
42    /// The type of the storage this readable uses.
43    type Storage: AnyStorage;
44
45    /// Try to get a reference to the value without checking the lifetime. This will subscribe the current scope to the signal.
46    ///
47    /// NOTE: This method is completely safe because borrow checking is done at runtime.
48    fn try_read_unchecked(
49        &self,
50    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
51    where
52        Self::Target: 'static;
53
54    /// Try to peek the current value of the signal without subscribing to updates. If the value has
55    /// been dropped, this will return an error.
56    ///
57    /// NOTE: This method is completely safe because borrow checking is done at runtime.
58    fn try_peek_unchecked(
59        &self,
60    ) -> Result<ReadableRef<'static, Self>, generational_box::BorrowError>
61    where
62        Self::Target: 'static;
63
64    /// Get the underlying subscriber list for this readable. This is used to track when the value changes and notify subscribers.
65    fn subscribers(&self) -> Subscribers
66    where
67        Self::Target: 'static;
68}
69
70/// An extension trait for `Readable` types that provides some convenience methods.
71pub trait ReadableExt: Readable {
72    /// Get the current value of the state. If this is a signal, this will subscribe the current scope to the signal.
73    /// If the value has been dropped, this will panic. Calling this on a Signal is the same as
74    /// using the signal() syntax to read and subscribe to its value
75    #[track_caller]
76    fn read(&self) -> ReadableRef<'_, Self>
77    where
78        Self::Target: 'static,
79    {
80        self.try_read().unwrap()
81    }
82
83    /// Try to get the current value of the state. If this is a signal, this will subscribe the current scope to the signal.
84    #[track_caller]
85    fn try_read(&self) -> Result<ReadableRef<'_, Self>, generational_box::BorrowError>
86    where
87        Self::Target: 'static,
88    {
89        self.try_read_unchecked()
90            .map(Self::Storage::downcast_lifetime_ref)
91    }
92
93    /// Get a reference to the value without checking the lifetime. This will subscribe the current scope to the signal.
94    ///
95    /// NOTE: This method is completely safe because borrow checking is done at runtime.
96    #[track_caller]
97    fn read_unchecked(&self) -> ReadableRef<'static, Self>
98    where
99        Self::Target: 'static,
100    {
101        self.try_read_unchecked().unwrap()
102    }
103
104    /// Get the current value of the state without subscribing to updates. If the value has been dropped, this will panic.
105    ///
106    /// # Example
107    /// ```rust
108    /// # use dioxus::prelude::*;
109    /// fn MyComponent(mut count: Signal<i32>) -> Element {
110    ///     let mut event_source = use_signal(|| None);
111    ///     let doubled = use_memo(move || {
112    ///         // We want to log the value of the event_source, but we don't need to rerun the doubled value if the event_source changes (because the value of doubled doesn't depend on the event_source)
113    ///         // We can read the value with peek without subscribing to updates
114    ///         let source = event_source.peek();
115    ///         tracing::info!("Clicked: {source:?}");
116    ///         count() * 2
117    ///     });
118    ///     rsx! {
119    ///         div { "Count: {count}" }
120    ///         div { "Doubled: {doubled}" }
121    ///         button {
122    ///             onclick: move |_| {
123    ///                 event_source.set(Some("Click me button"));
124    ///                 count += 1;
125    ///             },
126    ///             "Click me"
127    ///         }
128    ///         button {
129    ///             onclick: move |_| {
130    ///                 event_source.set(Some("Double me button"));
131    ///                 count += 1;
132    ///             },
133    ///             "Double me"
134    ///         }
135    ///     }
136    /// }
137    /// ```
138    #[track_caller]
139    fn peek(&self) -> ReadableRef<'_, Self>
140    where
141        Self::Target: 'static,
142    {
143        Self::Storage::downcast_lifetime_ref(self.peek_unchecked())
144    }
145
146    /// Try to peek the current value of the signal without subscribing to updates. If the value has
147    /// been dropped, this will return an error.
148    #[track_caller]
149    fn try_peek(&self) -> Result<ReadableRef<'_, Self>, generational_box::BorrowError>
150    where
151        Self::Target: 'static,
152    {
153        self.try_peek_unchecked()
154            .map(Self::Storage::downcast_lifetime_ref)
155    }
156
157    /// Get the current value of the signal without checking the lifetime. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
158    ///
159    /// If the signal has been dropped, this will panic.
160    #[track_caller]
161    fn peek_unchecked(&self) -> ReadableRef<'static, Self>
162    where
163        Self::Target: 'static,
164    {
165        self.try_peek_unchecked().unwrap()
166    }
167
168    /// Map the references of the readable value to a new type. This lets you provide a view
169    /// into the readable value without creating a new signal or cloning the value.
170    ///
171    /// Anything that subscribes to the readable value will be rerun whenever the original value changes, even if the view does not
172    /// change. If you want to memorize the view, you can use a [`crate::Memo`] instead. For fine grained scoped updates, use
173    /// stores instead
174    ///
175    /// # Example
176    /// ```rust
177    /// # use dioxus::prelude::*;
178    /// fn List(list: Signal<Vec<i32>>) -> Element {
179    ///     rsx! {
180    ///         for index in 0..list.len() {
181    ///             // We can use the `map` method to provide a view into the single item in the list that the child component will render
182    ///             Item { item: list.map(move |v| &v[index]) }
183    ///         }
184    ///     }
185    /// }
186    ///
187    /// // The child component doesn't need to know that the mapped value is coming from a list
188    /// #[component]
189    /// fn Item(item: ReadSignal<i32>) -> Element {
190    ///     rsx! {
191    ///         div { "Item: {item}" }
192    ///     }
193    /// }
194    /// ```
195    fn map<F, O>(self, f: F) -> MappedSignal<O, Self, F>
196    where
197        Self: Clone + Sized,
198        F: Fn(&Self::Target) -> &O,
199    {
200        MappedSignal::new(self, f)
201    }
202
203    /// Clone the inner value and return it. If the value has been dropped, this will panic.
204    #[track_caller]
205    fn cloned(&self) -> Self::Target
206    where
207        Self::Target: Clone + 'static,
208    {
209        self.read().clone()
210    }
211
212    /// Run a function with a reference to the value. If the value has been dropped, this will panic.
213    #[track_caller]
214    fn with<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
215    where
216        Self::Target: 'static,
217    {
218        f(&*self.read())
219    }
220
221    /// Run a function with a reference to the value. If the value has been dropped, this will panic.
222    #[track_caller]
223    fn with_peek<O>(&self, f: impl FnOnce(&Self::Target) -> O) -> O
224    where
225        Self::Target: 'static,
226    {
227        f(&*self.peek())
228    }
229
230    /// Index into the inner value and return a reference to the result. If the value has been dropped or the index is invalid, this will panic.
231    #[track_caller]
232    fn index<I>(
233        &self,
234        index: I,
235    ) -> ReadableRef<'_, Self, <Self::Target as std::ops::Index<I>>::Output>
236    where
237        Self::Target: std::ops::Index<I> + 'static,
238    {
239        <Self::Storage as AnyStorage>::map(self.read(), |v| v.index(index))
240    }
241
242    /// SAFETY: You must call this function directly with `self` as the argument.
243    /// This function relies on the size of the object you return from the deref
244    /// being the same as the object you pass in
245    #[doc(hidden)]
246    unsafe fn deref_impl<'a>(&self) -> &'a dyn Fn() -> Self::Target
247    where
248        Self: Sized + 'a,
249        Self::Target: Clone + 'static,
250    {
251        // https://github.com/dtolnay/case-studies/tree/master/callable-types
252
253        // First we create a closure that captures something with the Same in memory layout as Self (MaybeUninit<Self>).
254        let uninit_callable = MaybeUninit::<Self>::uninit();
255        // Then move that value into the closure. We assume that the closure now has a in memory layout of Self.
256        let uninit_closure = move || Self::read(unsafe { &*uninit_callable.as_ptr() }).clone();
257
258        // Check that the size of the closure is the same as the size of Self in case the compiler changed the layout of the closure.
259        let size_of_closure = std::mem::size_of_val(&uninit_closure);
260        assert_eq!(size_of_closure, std::mem::size_of::<Self>());
261
262        // Then cast the lifetime of the closure to the lifetime of &self.
263        fn cast_lifetime<'a, T>(_a: &T, b: &'a T) -> &'a T {
264            b
265        }
266        let reference_to_closure = cast_lifetime(
267            {
268                // The real closure that we will never use.
269                &uninit_closure
270            },
271            #[allow(clippy::missing_transmute_annotations)]
272            // We transmute self into a reference to the closure. This is safe because we know that the closure has the same memory layout as Self so &Closure == &Self.
273            unsafe {
274                std::mem::transmute(self)
275            },
276        );
277
278        // Cast the closure to a trait object.
279        reference_to_closure as &_
280    }
281}
282
283impl<R: Readable + ?Sized> ReadableExt for R {}
284
285/// An extension trait for `Readable` types that can be boxed into a trait object.
286pub trait ReadableBoxExt: Readable<Storage = UnsyncStorage> {
287    /// Box the readable value into a trait object. This is useful for passing around readable values without knowing their concrete type.
288    fn boxed(self) -> ReadSignal<Self::Target>
289    where
290        Self: Sized + 'static,
291    {
292        ReadSignal::new(self)
293    }
294}
295impl<R: Readable<Storage = UnsyncStorage> + ?Sized> ReadableBoxExt for R {}
296
297/// An extension trait for `Readable<Vec<T>>` that provides some convenience methods.
298pub trait ReadableVecExt<T>: Readable<Target = Vec<T>> {
299    /// Returns the length of the inner vector.
300    #[track_caller]
301    fn len(&self) -> usize
302    where
303        T: 'static,
304    {
305        self.with(|v| v.len())
306    }
307
308    /// Returns true if the inner vector is empty.
309    #[track_caller]
310    fn is_empty(&self) -> bool
311    where
312        T: 'static,
313    {
314        self.with(|v| v.is_empty())
315    }
316
317    /// Get the first element of the inner vector.
318    #[track_caller]
319    fn first(&self) -> Option<ReadableRef<'_, Self, T>>
320    where
321        T: 'static,
322    {
323        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.first())
324    }
325
326    /// Get the last element of the inner vector.
327    #[track_caller]
328    fn last(&self) -> Option<ReadableRef<'_, Self, T>>
329    where
330        T: 'static,
331    {
332        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.last())
333    }
334
335    /// Get the element at the given index of the inner vector.
336    #[track_caller]
337    fn get(&self, index: usize) -> Option<ReadableRef<'_, Self, T>>
338    where
339        T: 'static,
340    {
341        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.get(index))
342    }
343
344    /// Get an iterator over the values of the inner vector.
345    #[track_caller]
346    fn iter(&self) -> ReadableValueIterator<'_, Self>
347    where
348        Self: Sized,
349    {
350        ReadableValueIterator {
351            index: 0,
352            value: self,
353        }
354    }
355}
356
357/// An iterator over the values of a `Readable<Vec<T>>`.
358pub struct ReadableValueIterator<'a, R> {
359    index: usize,
360    value: &'a R,
361}
362
363impl<'a, T: 'static, R: Readable<Target = Vec<T>>> Iterator for ReadableValueIterator<'a, R> {
364    type Item = ReadableRef<'a, R, T>;
365
366    fn next(&mut self) -> Option<Self::Item> {
367        let index = self.index;
368        self.index += 1;
369        self.value.get(index)
370    }
371}
372
373impl<T, R> ReadableVecExt<T> for R where R: Readable<Target = Vec<T>> {}
374
375/// An extension trait for `Readable<Option<T>>` that provides some convenience methods.
376pub trait ReadableOptionExt<T>: Readable<Target = Option<T>> {
377    /// Unwraps the inner value and clones it.
378    #[track_caller]
379    fn unwrap(&self) -> T
380    where
381        T: Clone + 'static,
382    {
383        self.as_ref().unwrap().clone()
384    }
385
386    /// Attempts to read the inner value of the Option.
387    #[track_caller]
388    fn as_ref(&self) -> Option<ReadableRef<'_, Self, T>>
389    where
390        T: 'static,
391    {
392        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref())
393    }
394}
395
396impl<T, R> ReadableOptionExt<T> for R where R: Readable<Target = Option<T>> {}
397
398/// An extension trait for `Readable<Option<T>>` that provides some convenience methods.
399pub trait ReadableResultExt<T, E>: Readable<Target = Result<T, E>> {
400    /// Unwraps the inner value and clones it.
401    #[track_caller]
402    fn unwrap(&self) -> T
403    where
404        T: Clone + 'static,
405        E: 'static,
406    {
407        self.as_ref()
408            .unwrap_or_else(|_| panic!("Tried to unwrap a Result that was an error"))
409            .clone()
410    }
411
412    /// Attempts to read the inner value of the Option.
413    #[track_caller]
414    fn as_ref(&self) -> Result<ReadableRef<'_, Self, T>, ReadableRef<'_, Self, E>>
415    where
416        T: 'static,
417        E: 'static,
418    {
419        <Self::Storage as AnyStorage>::try_map(self.read(), |v| v.as_ref().ok()).ok_or(
420            <Self::Storage as AnyStorage>::map(self.read(), |v| v.as_ref().err().unwrap()),
421        )
422    }
423}
424
425impl<T, E, R> ReadableResultExt<T, E> for R where R: Readable<Target = Result<T, E>> {}