dioxus_signals/
signal.rs

1use crate::{default_impl, fmt_impls, write_impls, Global};
2use crate::{read::*, write::*, CopyValue, GlobalMemo, GlobalSignal, ReadableRef};
3use crate::{Memo, WritableRef};
4use dioxus_core::prelude::*;
5use generational_box::{AnyStorage, BorrowResult, Storage, SyncStorage, UnsyncStorage};
6use std::sync::Arc;
7use std::{
8    any::Any,
9    collections::HashSet,
10    ops::{Deref, DerefMut},
11    sync::Mutex,
12};
13
14#[doc = include_str!("../docs/signals.md")]
15#[doc(alias = "State")]
16#[doc(alias = "UseState")]
17#[doc(alias = "UseRef")]
18pub struct Signal<T: 'static, S: Storage<SignalData<T>> = UnsyncStorage> {
19    pub(crate) inner: CopyValue<SignalData<T>, S>,
20}
21
22/// A signal that can safely shared between threads.
23#[doc(alias = "SendSignal")]
24#[doc(alias = "UseRwLock")]
25#[doc(alias = "UseRw")]
26#[doc(alias = "UseMutex")]
27pub type SyncSignal<T> = Signal<T, SyncStorage>;
28
29/// The data stored for tracking in a signal.
30pub struct SignalData<T> {
31    pub(crate) subscribers: Arc<Mutex<HashSet<ReactiveContext>>>,
32    pub(crate) value: T,
33}
34
35impl<T: 'static> Signal<T> {
36    /// Creates a new [`Signal`]. Signals are a Copy state management solution with automatic dependency tracking.
37    ///
38    /// <div class="warning">
39    ///
40    /// This function should generally only be called inside hooks. The signal that this function creates is owned by the current component and will only be dropped when the component is dropped. If you call this function outside of a hook many times, you will leak memory until the component is dropped.
41    ///
42    /// ```rust
43    /// # use dioxus::prelude::*;
44    /// fn MyComponent() {
45    ///     // ❌ Every time MyComponent runs, it will create a new signal that is only dropped when MyComponent is dropped
46    ///     let signal = Signal::new(0);
47    ///     use_context_provider(|| signal);
48    ///     // ✅ Since the use_context_provider hook only runs when the component is created, the signal will only be created once and it will be dropped when MyComponent is dropped
49    ///     let signal = use_context_provider(|| Signal::new(0));
50    /// }
51    /// ```
52    ///
53    /// </div>
54    #[track_caller]
55    pub fn new(value: T) -> Self {
56        Self::new_maybe_sync(value)
57    }
58
59    /// Create a new signal with a custom owner scope. The signal will be dropped when the owner scope is dropped instead of the current scope.
60    #[track_caller]
61    pub fn new_in_scope(value: T, owner: ScopeId) -> Self {
62        Self::new_maybe_sync_in_scope(value, owner)
63    }
64
65    /// Creates a new [`GlobalSignal`] that can be used anywhere inside your dioxus app. This signal will automatically be created once per app the first time you use it.
66    ///
67    /// # Example
68    /// ```rust, no_run
69    /// # use dioxus::prelude::*;
70    /// // Create a new global signal that can be used anywhere in your app
71    /// static SIGNAL: GlobalSignal<i32> = Signal::global(|| 0);
72    ///
73    /// fn App() -> Element {
74    ///     rsx! {
75    ///         button {
76    ///             onclick: move |_| *SIGNAL.write() += 1,
77    ///             "{SIGNAL}"
78    ///         }
79    ///     }
80    /// }
81    /// ```
82    ///
83    /// <div class="warning">
84    ///
85    /// Global signals are generally not recommended for use in libraries because it makes it more difficult to allow multiple instances of components you define in your library.
86    ///
87    /// </div>
88    #[track_caller]
89    pub const fn global(constructor: fn() -> T) -> GlobalSignal<T> {
90        Global::new(constructor)
91    }
92}
93
94impl<T: PartialEq + 'static> Signal<T> {
95    /// Creates a new [`GlobalMemo`] that can be used anywhere inside your dioxus app. This memo will automatically be created once per app the first time you use it.
96    ///
97    /// # Example
98    /// ```rust, no_run
99    /// # use dioxus::prelude::*;
100    /// static SIGNAL: GlobalSignal<i32> = Signal::global(|| 0);
101    /// // Create a new global memo that can be used anywhere in your app
102    /// static DOUBLED: GlobalMemo<i32> = Signal::global_memo(|| SIGNAL() * 2);
103    ///
104    /// fn App() -> Element {
105    ///     rsx! {
106    ///         button {
107    ///             // When SIGNAL changes, the memo will update because the SIGNAL is read inside DOUBLED
108    ///             onclick: move |_| *SIGNAL.write() += 1,
109    ///             "{DOUBLED}"
110    ///         }
111    ///     }
112    /// }
113    /// ```
114    ///
115    /// <div class="warning">
116    ///
117    /// Global memos are generally not recommended for use in libraries because it makes it more difficult to allow multiple instances of components you define in your library.
118    ///
119    /// </div>
120    #[track_caller]
121    pub const fn global_memo(constructor: fn() -> T) -> GlobalMemo<T>
122    where
123        T: PartialEq,
124    {
125        GlobalMemo::new(constructor)
126    }
127
128    /// Creates a new unsync Selector. The selector will be run immediately and whenever any signal it reads changes.
129    ///
130    /// Selectors can be used to efficiently compute derived data from signals.
131    #[track_caller]
132    pub fn memo(f: impl FnMut() -> T + 'static) -> Memo<T> {
133        Memo::new(f)
134    }
135
136    /// Creates a new unsync Selector with an explicit location. The selector will be run immediately and whenever any signal it reads changes.
137    ///
138    /// Selectors can be used to efficiently compute derived data from signals.
139    pub fn memo_with_location(
140        f: impl FnMut() -> T + 'static,
141        location: &'static std::panic::Location<'static>,
142    ) -> Memo<T> {
143        Memo::new_with_location(f, location)
144    }
145}
146
147impl<T: 'static, S: Storage<SignalData<T>>> Signal<T, S> {
148    /// Creates a new Signal. Signals are a Copy state management solution with automatic dependency tracking.
149    #[track_caller]
150    #[tracing::instrument(skip(value))]
151    pub fn new_maybe_sync(value: T) -> Self {
152        Self {
153            inner: CopyValue::<SignalData<T>, S>::new_maybe_sync(SignalData {
154                subscribers: Default::default(),
155                value,
156            }),
157        }
158    }
159
160    /// Creates a new Signal with an explicit caller. Signals are a Copy state management solution with automatic dependency tracking.
161    ///
162    /// This method can be used to provide the correct caller information for signals that are created in closures:
163    ///
164    /// ```rust
165    /// # use dioxus::prelude::*;
166    /// #[track_caller]
167    /// fn use_my_signal(function: impl FnOnce() -> i32) -> Signal<i32> {
168    ///     // We capture the caller information outside of the closure so that it points to the caller of use_my_custom_hook instead of the closure
169    ///     let caller = std::panic::Location::caller();
170    ///     use_hook(move || Signal::new_with_caller(function(), caller))
171    /// }
172    /// ```
173    pub fn new_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
174        Self {
175            inner: CopyValue::new_with_caller(
176                SignalData {
177                    subscribers: Default::default(),
178                    value,
179                },
180                caller,
181            ),
182        }
183    }
184
185    /// Create a new Signal without an owner. This will leak memory if you don't manually drop it.
186    pub fn leak_with_caller(value: T, caller: &'static std::panic::Location<'static>) -> Self {
187        Self {
188            inner: CopyValue::leak_with_caller(
189                SignalData {
190                    subscribers: Default::default(),
191                    value,
192                },
193                caller,
194            ),
195        }
196    }
197
198    /// Create a new signal with a custom owner scope. The signal will be dropped when the owner scope is dropped instead of the current scope.
199    #[track_caller]
200    #[tracing::instrument(skip(value))]
201    pub fn new_maybe_sync_in_scope(value: T, owner: ScopeId) -> Self {
202        Self::new_maybe_sync_in_scope_with_caller(value, owner, std::panic::Location::caller())
203    }
204
205    /// Create a new signal with a custom owner scope and a custom caller. The signal will be dropped when the owner scope is dropped instead of the current scope.
206    #[tracing::instrument(skip(value))]
207    pub fn new_maybe_sync_in_scope_with_caller(
208        value: T,
209        owner: ScopeId,
210        caller: &'static std::panic::Location<'static>,
211    ) -> Self {
212        Self {
213            inner: CopyValue::<SignalData<T>, S>::new_maybe_sync_in_scope_with_caller(
214                SignalData {
215                    subscribers: Default::default(),
216                    value,
217                },
218                owner,
219                caller,
220            ),
221        }
222    }
223
224    /// Point to another signal. This will subscribe the other signal to all subscribers of this signal.
225    pub fn point_to(&self, other: Self) -> BorrowResult {
226        #[allow(clippy::mutable_key_type)]
227        let this_subscribers = self.inner.value.read().subscribers.lock().unwrap().clone();
228        let other_read = other.inner.value.read();
229        for subscriber in this_subscribers.iter() {
230            subscriber.subscribe(other_read.subscribers.clone());
231        }
232        self.inner.point_to(other.inner)
233    }
234
235    /// Drop the value out of the signal, invalidating the signal in the process.
236    pub fn manually_drop(&self) {
237        self.inner.manually_drop()
238    }
239
240    /// Get the scope the signal was created in.
241    pub fn origin_scope(&self) -> ScopeId {
242        self.inner.origin_scope()
243    }
244
245    fn update_subscribers(&self) {
246        {
247            let inner = self.inner.read();
248
249            // We cannot hold the subscribers lock while calling mark_dirty, because mark_dirty can run user code which may cause a new subscriber to be added. If we hold the lock, we will deadlock.
250            #[allow(clippy::mutable_key_type)]
251            let mut subscribers = std::mem::take(&mut *inner.subscribers.lock().unwrap());
252            subscribers.retain(|reactive_context| reactive_context.mark_dirty());
253            // Extend the subscribers list instead of overwriting it in case a subscriber is added while reactive contexts are marked dirty
254            inner.subscribers.lock().unwrap().extend(subscribers);
255        }
256    }
257
258    /// Get the generational id of the signal.
259    pub fn id(&self) -> generational_box::GenerationalBoxId {
260        self.inner.id()
261    }
262
263    /// **This pattern is no longer recommended. Prefer [`peek`](Signal::peek) or creating new signals instead.**
264    ///
265    /// This function is the equivalent of the [write_silent](https://docs.rs/dioxus/latest/dioxus/prelude/struct.UseRef.html#method.write_silent) method on use_ref.
266    ///
267    /// ## What you should use instead
268    ///
269    /// ### Reading and Writing to data in the same scope
270    ///
271    /// Reading and writing to the same signal in the same scope will cause that scope to rerun forever:
272    /// ```rust, no_run
273    /// # use dioxus::prelude::*;
274    /// let mut signal = use_signal(|| 0);
275    /// // This makes the scope rerun whenever we write to the signal
276    /// println!("{}", *signal.read());
277    /// // This will rerun the scope because we read the signal earlier in the same scope
278    /// *signal.write() += 1;
279    /// ```
280    ///
281    /// You may have used the write_silent method to avoid this infinite loop with use_ref like this:
282    /// ```rust, no_run
283    /// # use dioxus::prelude::*;
284    /// let signal = use_signal(|| 0);
285    /// // This makes the scope rerun whenever we write to the signal
286    /// println!("{}", *signal.read());
287    /// // Write silent will not rerun any subscribers
288    /// *signal.write_silent() += 1;
289    /// ```
290    ///
291    /// Instead you can use the [`peek`](Signal::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.
292    /// ```rust, no_run
293    /// # use dioxus::prelude::*;
294    /// let mut signal = use_signal(|| 0);
295    /// // Peek will read the value but not subscribe to the current scope
296    /// println!("{}", *signal.peek());
297    /// // Write will update any subscribers which does not include the current scope
298    /// *signal.write() += 1;
299    /// ```
300    ///
301    /// ### Reading and Writing to different data
302    ///
303    ///
304    ///
305    /// ## Why is this pattern no longer recommended?
306    ///
307    /// This pattern is no longer recommended because it is very easy to allow your state and UI to grow out of sync. `write_silent` globally opts out of automatic state updates which can be difficult to reason about.
308    ///
309    ///
310    /// Lets take a look at an example:
311    /// main.rs:
312    /// ```rust, no_run
313    /// # use dioxus::prelude::*;
314    /// # fn Child() -> Element { unimplemented!() }
315    /// fn app() -> Element {
316    ///     let signal = use_context_provider(|| Signal::new(0));
317    ///
318    ///     // We want to log the value of the signal whenever the app component reruns
319    ///     println!("{}", *signal.read());
320    ///
321    ///     rsx! {
322    ///         button {
323    ///             // If we don't want to rerun the app component when the button is clicked, we can use write_silent
324    ///             onclick: move |_| *signal.write_silent() += 1,
325    ///             "Increment"
326    ///         }
327    ///         Child {}
328    ///     }
329    /// }
330    /// ```
331    /// child.rs:
332    /// ```rust, no_run
333    /// # use dioxus::prelude::*;
334    /// fn Child() -> Element {
335    ///     let signal: Signal<i32> = use_context();
336    ///
337    ///     // It is difficult to tell that changing the button to use write_silent in the main.rs file will cause UI to be out of sync in a completely different file
338    ///     rsx! {
339    ///         "{signal}"
340    ///     }
341    /// }
342    /// ```
343    ///
344    /// Instead [`peek`](Signal::peek) locally opts out of automatic state updates explicitly for a specific read which is easier to reason about.
345    ///
346    /// Here is the same example using peek:
347    /// main.rs:
348    /// ```rust, no_run
349    /// # use dioxus::prelude::*;
350    /// # fn Child() -> Element { unimplemented!() }
351    /// fn app() -> Element {
352    ///     let mut signal = use_context_provider(|| Signal::new(0));
353    ///
354    ///     // We want to log the value of the signal whenever the app component reruns, but we don't want to rerun the app component when the signal is updated so we use peek instead of read
355    ///     println!("{}", *signal.peek());
356    ///
357    ///     rsx! {
358    ///         button {
359    ///             // We can use write like normal and update the child component automatically
360    ///             onclick: move |_| *signal.write() += 1,
361    ///             "Increment"
362    ///         }
363    ///         Child {}
364    ///     }
365    /// }
366    /// ```
367    /// child.rs:
368    /// ```rust, no_run
369    /// # use dioxus::prelude::*;
370    /// fn Child() -> Element {
371    ///     let signal: Signal<i32> = use_context();
372    ///
373    ///     rsx! {
374    ///         "{signal}"
375    ///     }
376    /// }
377    /// ```
378    #[track_caller]
379    #[deprecated = "This pattern is no longer recommended. Prefer `peek` or creating new signals instead."]
380    pub fn write_silent(&self) -> S::Mut<'static, T> {
381        S::map_mut(self.inner.write_unchecked(), |inner| &mut inner.value)
382    }
383}
384
385impl<T, S: Storage<SignalData<T>>> Readable for Signal<T, S> {
386    type Target = T;
387    type Storage = S;
388
389    #[track_caller]
390    fn try_read_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
391        let inner = self.inner.try_read_unchecked()?;
392
393        if let Some(reactive_context) = ReactiveContext::current() {
394            tracing::trace!("Subscribing to the reactive context {}", reactive_context);
395            reactive_context.subscribe(inner.subscribers.clone());
396        }
397
398        Ok(S::map(inner, |v| &v.value))
399    }
400
401    /// Get the current value of the signal. **Unlike read, this will not subscribe the current scope to the signal which can cause parts of your UI to not update.**
402    ///
403    /// If the signal has been dropped, this will panic.
404    #[track_caller]
405    fn try_peek_unchecked(&self) -> BorrowResult<ReadableRef<'static, Self>> {
406        self.inner
407            .try_read_unchecked()
408            .map(|inner| S::map(inner, |v| &v.value))
409    }
410}
411
412impl<T: 'static, S: Storage<SignalData<T>>> Writable for Signal<T, S> {
413    type Mut<'a, R: ?Sized + 'static> = Write<'a, R, S>;
414
415    fn map_mut<I: ?Sized, U: ?Sized + 'static, F: FnOnce(&mut I) -> &mut U>(
416        ref_: Self::Mut<'_, I>,
417        f: F,
418    ) -> Self::Mut<'_, U> {
419        Write::map(ref_, f)
420    }
421
422    fn try_map_mut<
423        I: ?Sized + 'static,
424        U: ?Sized + 'static,
425        F: FnOnce(&mut I) -> Option<&mut U>,
426    >(
427        ref_: Self::Mut<'_, I>,
428        f: F,
429    ) -> Option<Self::Mut<'_, U>> {
430        Write::filter_map(ref_, f)
431    }
432
433    fn downcast_lifetime_mut<'a: 'b, 'b, R: ?Sized + 'static>(
434        mut_: Self::Mut<'a, R>,
435    ) -> Self::Mut<'b, R> {
436        Write::downcast_lifetime(mut_)
437    }
438
439    #[track_caller]
440    fn try_write_unchecked(
441        &self,
442    ) -> Result<WritableRef<'static, Self>, generational_box::BorrowMutError> {
443        #[cfg(debug_assertions)]
444        let origin = std::panic::Location::caller();
445        self.inner.try_write_unchecked().map(|inner| {
446            let borrow = S::map_mut(inner, |v| &mut v.value);
447            Write {
448                write: borrow,
449                drop_signal: Box::new(SignalSubscriberDrop {
450                    signal: *self,
451                    #[cfg(debug_assertions)]
452                    origin,
453                }),
454            }
455        })
456    }
457}
458
459impl<T> IntoAttributeValue for Signal<T>
460where
461    T: Clone + IntoAttributeValue,
462{
463    fn into_value(self) -> dioxus_core::AttributeValue {
464        self.with(|f| f.clone().into_value())
465    }
466}
467
468impl<T> IntoDynNode for Signal<T>
469where
470    T: Clone + IntoDynNode,
471{
472    fn into_dyn_node(self) -> dioxus_core::DynamicNode {
473        self().into_dyn_node()
474    }
475}
476
477impl<T: 'static, S: Storage<SignalData<T>>> PartialEq for Signal<T, S> {
478    fn eq(&self, other: &Self) -> bool {
479        self.inner == other.inner
480    }
481}
482
483impl<T: 'static, S: Storage<SignalData<T>>> Eq for Signal<T, S> {}
484
485/// Allow calling a signal with signal() syntax
486///
487/// Currently only limited to copy types, though could probably specialize for string/arc/rc
488impl<T: Clone, S: Storage<SignalData<T>> + 'static> Deref for Signal<T, S> {
489    type Target = dyn Fn() -> T;
490
491    fn deref(&self) -> &Self::Target {
492        unsafe { Readable::deref_impl(self) }
493    }
494}
495
496#[cfg(feature = "serialize")]
497impl<T: serde::Serialize + 'static, Store: Storage<SignalData<T>>> serde::Serialize
498    for Signal<T, Store>
499{
500    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
501        self.read().serialize(serializer)
502    }
503}
504
505#[cfg(feature = "serialize")]
506impl<'de, T: serde::Deserialize<'de> + 'static, Store: Storage<SignalData<T>>>
507    serde::Deserialize<'de> for Signal<T, Store>
508{
509    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
510        Ok(Self::new_maybe_sync(T::deserialize(deserializer)?))
511    }
512}
513
514/// A mutable reference to a signal's value. This reference acts similarly to [`std::cell::RefMut`], but it has extra debug information
515/// and integrates with the reactive system to automatically update dependents.
516///
517/// [`Write`] implements [`DerefMut`] which means you can call methods on the inner value just like you would on a mutable reference
518/// to the inner value. If you need to get the inner reference directly, you can call [`Write::deref_mut`].
519///
520/// # Example
521/// ```rust
522/// # use dioxus::prelude::*;
523/// fn app() -> Element {
524///     let mut value = use_signal(|| String::from("hello"));
525///     
526///     rsx! {
527///         button {
528///             onclick: move |_| {
529///                 let mut mutable_reference = value.write();
530///
531///                 // You call methods like `push_str` on the reference just like you would with the inner String
532///                 mutable_reference.push_str("world");
533///             },
534///             "Click to add world to the string"
535///         }
536///         div { "{value}" }
537///     }
538/// }
539/// ```
540///
541/// ## Matching on Write
542///
543/// You need to get the inner mutable reference with [`Write::deref_mut`] before you match the inner value. If you try to match
544/// without calling [`Write::deref_mut`], you will get an error like this:
545///
546/// ```compile_fail
547/// # use dioxus::prelude::*;
548/// #[derive(Debug)]
549/// enum Colors {
550///     Red(u32),
551///     Green
552/// }
553/// fn app() -> Element {
554///     let mut value = use_signal(|| Colors::Red(0));
555///
556///     rsx! {
557///         button {
558///             onclick: move |_| {
559///                 let mut mutable_reference = value.write();
560///
561///                 match mutable_reference {
562///                     // Since we are matching on the `Write` type instead of &mut Colors, we can't match on the enum directly
563///                     Colors::Red(brightness) => *brightness += 1,
564///                     Colors::Green => {}
565///                 }
566///             },
567///             "Click to add brightness to the red color"
568///         }
569///         div { "{value:?}" }
570///     }
571/// }
572/// ```
573///
574/// ```text
575/// error[E0308]: mismatched types
576///   --> src/main.rs:18:21
577///    |
578/// 16 |                 match mutable_reference {
579///    |                       ----------------- this expression has type `dioxus::prelude::Write<'_, Colors>`
580/// 17 |                     // Since we are matching on the `Write` t...
581/// 18 |                     Colors::Red(brightness) => *brightness += 1,
582///    |                     ^^^^^^^^^^^^^^^^^^^^^^^ expected `Write<'_, Colors>`, found `Colors`
583///    |
584///    = note: expected struct `dioxus::prelude::Write<'_, Colors, >`
585///                found enum `Colors`
586/// ```
587///
588/// Instead, you need to call deref mut on the reference to get the inner value **before** you match on it:
589///
590/// ```rust
591/// use std::ops::DerefMut;
592/// # use dioxus::prelude::*;
593/// #[derive(Debug)]
594/// enum Colors {
595///     Red(u32),
596///     Green
597/// }
598/// fn app() -> Element {
599///     let mut value = use_signal(|| Colors::Red(0));
600///
601///     rsx! {
602///         button {
603///             onclick: move |_| {
604///                 let mut mutable_reference = value.write();
605///
606///                 // DerefMut converts the `Write` into a `&mut Colors`
607///                 match mutable_reference.deref_mut() {
608///                     // Now we can match on the inner value
609///                     Colors::Red(brightness) => *brightness += 1,
610///                     Colors::Green => {}
611///                 }
612///             },
613///             "Click to add brightness to the red color"
614///         }
615///         div { "{value:?}" }
616///     }
617/// }
618/// ```
619///
620/// ## Generics
621/// - T is the current type of the write
622/// - 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.
623pub struct Write<'a, T: ?Sized + 'static, S: AnyStorage = UnsyncStorage> {
624    write: S::Mut<'a, T>,
625    drop_signal: Box<dyn Any>,
626}
627
628impl<'a, T: ?Sized + 'static, S: AnyStorage> Write<'a, T, S> {
629    /// Map the mutable reference to the signal's value to a new type.
630    pub fn map<O: ?Sized>(myself: Self, f: impl FnOnce(&mut T) -> &mut O) -> Write<'a, O, S> {
631        let Self {
632            write, drop_signal, ..
633        } = myself;
634        Write {
635            write: S::map_mut(write, f),
636            drop_signal,
637        }
638    }
639
640    /// Try to map the mutable reference to the signal's value to a new type
641    pub fn filter_map<O: ?Sized>(
642        myself: Self,
643        f: impl FnOnce(&mut T) -> Option<&mut O>,
644    ) -> Option<Write<'a, O, S>> {
645        let Self {
646            write, drop_signal, ..
647        } = myself;
648        let write = S::try_map_mut(write, f);
649        write.map(|write| Write { write, drop_signal })
650    }
651
652    /// Downcast the lifetime of the mutable reference to the signal's value.
653    ///
654    /// This function enforces the variance of the lifetime parameter `'a` in Mut.  Rust will typically infer this cast with a concrete type, but it cannot with a generic type.
655    pub fn downcast_lifetime<'b>(mut_: Self) -> Write<'b, T, S>
656    where
657        'a: 'b,
658    {
659        Write {
660            write: S::downcast_lifetime_mut(mut_.write),
661            drop_signal: mut_.drop_signal,
662        }
663    }
664}
665
666impl<T: ?Sized + 'static, S: AnyStorage> Deref for Write<'_, T, S> {
667    type Target = T;
668
669    fn deref(&self) -> &Self::Target {
670        &self.write
671    }
672}
673
674impl<T: ?Sized, S: AnyStorage> DerefMut for Write<'_, T, S> {
675    fn deref_mut(&mut self) -> &mut Self::Target {
676        &mut self.write
677    }
678}
679
680struct SignalSubscriberDrop<T: 'static, S: Storage<SignalData<T>>> {
681    signal: Signal<T, S>,
682    #[cfg(debug_assertions)]
683    origin: &'static std::panic::Location<'static>,
684}
685
686#[allow(clippy::no_effect)]
687impl<T: 'static, S: Storage<SignalData<T>>> Drop for SignalSubscriberDrop<T, S> {
688    fn drop(&mut self) {
689        #[cfg(debug_assertions)]
690        {
691            tracing::trace!(
692                "Write on signal at {} finished, updating subscribers",
693                self.origin
694            );
695            crate::warnings::signal_write_in_component_body(self.origin);
696            crate::warnings::signal_read_and_write_in_reactive_scope::<T, S>(
697                self.origin,
698                self.signal,
699            );
700        }
701        self.signal.update_subscribers();
702    }
703}
704
705fmt_impls!(Signal<T, S: Storage<SignalData<T>>>);
706default_impl!(Signal<T, S: Storage<SignalData<T>>>);
707write_impls!(Signal<T, S: Storage<SignalData<T>>>);
708
709impl<T: 'static, S: Storage<SignalData<T>>> Clone for Signal<T, S> {
710    fn clone(&self) -> Self {
711        *self
712    }
713}
714
715impl<T: 'static, S: Storage<SignalData<T>>> Copy for Signal<T, S> {}