leptos_reactive/
signal.rs

1use crate::{
2    console_warn, create_isomorphic_effect, diagnostics, diagnostics::*,
3    macros::debug_warn, node::NodeId, on_cleanup, runtime::with_runtime,
4    Runtime,
5};
6use futures::Stream;
7use std::{
8    any::Any,
9    cell::RefCell,
10    fmt,
11    hash::{Hash, Hasher},
12    marker::PhantomData,
13    pin::Pin,
14    rc::Rc,
15};
16use thiserror::Error;
17
18macro_rules! impl_get_fn_traits {
19    ($($ty:ident $(($method_name:ident))?),*) => {
20        $(
21            #[cfg(feature = "nightly")]
22            impl<T: Clone> FnOnce<()> for $ty<T> {
23                type Output = T;
24
25                #[inline(always)]
26                extern "rust-call" fn call_once(self, _args: ()) -> Self::Output {
27                    impl_get_fn_traits!(@method_name self $($method_name)?)
28                }
29            }
30
31            #[cfg(feature = "nightly")]
32            impl<T: Clone> FnMut<()> for $ty<T> {
33                #[inline(always)]
34                extern "rust-call" fn call_mut(&mut self, _args: ()) -> Self::Output {
35                    impl_get_fn_traits!(@method_name self $($method_name)?)
36                }
37            }
38
39            #[cfg(feature = "nightly")]
40            impl<T: Clone> Fn<()> for $ty<T> {
41                #[inline(always)]
42                extern "rust-call" fn call(&self, _args: ()) -> Self::Output {
43                    impl_get_fn_traits!(@method_name self $($method_name)?)
44                }
45            }
46        )*
47    };
48    (@method_name $self:ident) => {
49        $self.get()
50    };
51    (@method_name $self:ident $ident:ident) => {
52        $self.$ident()
53    };
54}
55
56macro_rules! impl_set_fn_traits {
57    ($($ty:ident $($method_name:ident)?),*) => {
58        $(
59            #[cfg(feature = "nightly")]
60            impl<T> FnOnce<(T,)> for $ty<T> {
61                type Output = ();
62
63                #[inline(always)]
64                extern "rust-call" fn call_once(self, args: (T,)) -> Self::Output {
65                    impl_set_fn_traits!(@method_name self $($method_name)? args)
66                }
67            }
68
69            #[cfg(feature = "nightly")]
70            impl<T> FnMut<(T,)> for $ty<T> {
71                #[inline(always)]
72                extern "rust-call" fn call_mut(&mut self, args: (T,)) -> Self::Output {
73                    impl_set_fn_traits!(@method_name self $($method_name)? args)
74                }
75            }
76
77            #[cfg(feature = "nightly")]
78            impl<T> Fn<(T,)> for $ty<T> {
79                #[inline(always)]
80                extern "rust-call" fn call(&self, args: (T,)) -> Self::Output {
81                    impl_set_fn_traits!(@method_name self $($method_name)? args)
82                }
83            }
84        )*
85    };
86    (@method_name $self:ident $args:ident) => {
87        $self.set($args.0)
88    };
89    (@method_name $self:ident $ident:ident $args:ident) => {
90        $self.$ident($args.0)
91    };
92}
93
94impl_get_fn_traits![ReadSignal, RwSignal];
95impl_set_fn_traits![WriteSignal];
96
97/// This prelude imports all signal types as well as all signal
98/// traits needed to use those types.
99pub mod prelude {
100    pub use super::*;
101    pub use crate::{
102        memo::*, selector::*, signal_wrappers_read::*, signal_wrappers_write::*,
103    };
104}
105
106/// This trait allows getting an owned value of the signals
107/// inner type.
108pub trait SignalGet {
109    /// The value held by the signal.
110    type Value;
111
112    /// Clones and returns the current value of the signal, and subscribes
113    /// the running effect to this signal.
114    ///
115    /// # Panics
116    /// Panics if you try to access a signal that is owned by a reactive node that has been disposed.
117    #[track_caller]
118    fn get(&self) -> Self::Value;
119
120    /// Clones and returns the signal value, returning [`Some`] if the signal
121    /// is still alive, and [`None`] otherwise.
122    fn try_get(&self) -> Option<Self::Value>;
123}
124
125/// This trait allows obtaining an immutable reference to the signal's
126/// inner type.
127pub trait SignalWith {
128    /// The value held by the signal.
129    type Value;
130
131    /// Applies a function to the current value of the signal, and subscribes
132    /// the running effect to this signal.
133    ///
134    /// # Panics
135    /// Panics if you try to access a signal that is owned by a reactive node that has been disposed.
136    #[track_caller]
137    fn with<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> O;
138
139    /// Applies a function to the current value of the signal, and subscribes
140    /// the running effect to this signal. Returns [`Some`] if the signal is
141    /// valid and the function ran, otherwise returns [`None`].
142    fn try_with<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> Option<O>;
143
144    /// Subscribes to this signal in the current reactive scope without doing anything with its value.
145    fn track(&self) {
146        _ = self.try_with(|_| {});
147    }
148}
149
150/// This trait allows setting the value of a signal.
151pub trait SignalSet {
152    /// The value held by the signal.
153    type Value;
154
155    /// Sets the signal’s value and notifies subscribers.
156    ///
157    /// **Note:** `set()` does not auto-memoize, i.e., it will notify subscribers
158    /// even if the value has not actually changed.
159    #[track_caller]
160    fn set(&self, new_value: Self::Value);
161
162    /// Sets the signal’s value and notifies subscribers. Returns [`None`]
163    /// if the signal is still valid, [`Some(T)`] otherwise.
164    ///
165    /// **Note:** `set()` does not auto-memoize, i.e., it will notify subscribers
166    /// even if the value has not actually changed.
167    fn try_set(&self, new_value: Self::Value) -> Option<Self::Value>;
168}
169
170/// This trait allows updating the inner value of a signal.
171pub trait SignalUpdate {
172    /// The value held by the signal.
173    type Value;
174
175    /// Applies a function to the current value to mutate it in place
176    /// and notifies subscribers that the signal has changed.
177    ///
178    /// **Note:** `update()` does not auto-memoize, i.e., it will notify subscribers
179    /// even if the value has not actually changed.
180    #[track_caller]
181    fn update(&self, f: impl FnOnce(&mut Self::Value));
182
183    /// Applies a function to the current value to mutate it in place
184    /// and notifies subscribers that the signal has changed. Returns
185    /// [`Some(O)`] if the signal is still valid, [`None`] otherwise.
186    ///
187    /// **Note:** `update()` does not auto-memoize, i.e., it will notify subscribers
188    /// even if the value has not actually changed.
189    fn try_update<O>(&self, f: impl FnOnce(&mut Self::Value) -> O)
190        -> Option<O>;
191}
192
193/// Trait implemented for all signal types which you can `get` a value
194/// from, such as [`ReadSignal`],
195/// [`Memo`](crate::Memo), etc., which allows getting the inner value without
196/// subscribing to the current scope.
197pub trait SignalGetUntracked {
198    /// The value held by the signal.
199    type Value;
200
201    /// Gets the signal's value without creating a dependency on the
202    /// current scope.
203    ///
204    /// # Panics
205    /// Panics if you try to access a signal that is owned by a reactive node that has been disposed.
206    #[track_caller]
207    fn get_untracked(&self) -> Self::Value;
208
209    /// Gets the signal's value without creating a dependency on the
210    /// current scope. Returns [`Some(T)`] if the signal is still
211    /// valid, [`None`] otherwise.
212    fn try_get_untracked(&self) -> Option<Self::Value>;
213}
214
215/// This trait allows getting a reference to the signals inner value
216/// without creating a dependency on the signal.
217pub trait SignalWithUntracked {
218    /// The value held by the signal.
219    type Value;
220
221    /// Runs the provided closure with a reference to the current
222    /// value without creating a dependency on the current scope.
223    ///
224    /// # Panics
225    /// Panics if you try to access a signal that is owned by a reactive node that has been disposed.
226    #[track_caller]
227    fn with_untracked<O>(&self, f: impl FnOnce(&Self::Value) -> O) -> O;
228
229    /// Runs the provided closure with a reference to the current
230    /// value without creating a dependency on the current scope.
231    /// Returns [`Some(O)`] if the signal is still valid, [`None`]
232    /// otherwise.
233    #[track_caller]
234    fn try_with_untracked<O>(
235        &self,
236        f: impl FnOnce(&Self::Value) -> O,
237    ) -> Option<O>;
238}
239
240/// Trait implemented for all signal types which you can `set` the inner
241/// value, such as [`WriteSignal`] and [`RwSignal`], which allows setting
242/// the inner value without causing effects which depend on the signal
243/// from being run.
244pub trait SignalSetUntracked<T> {
245    /// Sets the signal's value without notifying dependents.
246    #[track_caller]
247    fn set_untracked(&self, new_value: T);
248
249    /// Attempts to set the signal if it's still valid. Returns [`None`]
250    /// if the signal was set, [`Some(T)`] otherwise.
251    #[track_caller]
252    fn try_set_untracked(&self, new_value: T) -> Option<T>;
253}
254
255/// This trait allows updating the signals value without causing
256/// dependant effects to run.
257pub trait SignalUpdateUntracked<T> {
258    /// Runs the provided closure with a mutable reference to the current
259    /// value without notifying dependents.
260    #[track_caller]
261    fn update_untracked(&self, f: impl FnOnce(&mut T));
262
263    /// Runs the provided closure with a mutable reference to the current
264    /// value without notifying dependents and returns
265    /// the value the closure returned.
266    fn try_update_untracked<O>(&self, f: impl FnOnce(&mut T) -> O)
267        -> Option<O>;
268}
269
270/// This trait allows converting a signal into a async [`Stream`].
271pub trait SignalStream<T> {
272    /// Generates a [`Stream`] that emits the new value of the signal
273    /// whenever it changes.
274    ///
275    /// # Panics
276    /// Panics if you try to access a signal that is owned by a reactive node that has been disposed.
277    // We're returning an opaque type until impl trait in trait
278    // positions are stabilized, and also so any underlying
279    // changes are non-breaking
280    #[track_caller]
281    fn to_stream(&self) -> Pin<Box<dyn Stream<Item = T>>>;
282}
283
284/// This trait allows disposing a signal before its owner has been disposed.
285pub trait SignalDispose {
286    /// Disposes of the signal. This:
287    /// 1. Detaches the signal from the reactive graph, preventing it from triggering
288    ///    further updates; and
289    /// 2. Drops the value contained in the signal.
290    #[track_caller]
291    fn dispose(self);
292}
293
294/// Creates a signal, the basic reactive primitive.
295///
296/// A signal is a piece of data that may change over time,
297/// and notifies other code when it has changed. This is the
298/// core primitive of Leptos’s reactive system.
299///
300/// Takes the initial value as an argument,
301/// and returns a tuple containing a [`ReadSignal`] and a [`WriteSignal`],
302/// each of which can be called as a function.
303///
304/// ```
305/// # use leptos_reactive::*;
306/// # let runtime = create_runtime();
307/// let (count, set_count) = create_signal(0);
308///
309/// // ✅ calling the getter clones and returns the value
310/// //    this can be `count()` on nightly
311/// assert_eq!(count.get(), 0);
312///
313/// // ✅ calling the setter sets the value
314/// //    this can be `set_count(1)` on nightly
315/// set_count.set(1);
316/// assert_eq!(count.get(), 1);
317///
318/// // ❌ you could call the getter within the setter
319/// // set_count.set(count.get() + 1);
320///
321/// // ✅ however it's more efficient to use .update() and mutate the value in place
322/// set_count.update(|count: &mut i32| *count += 1);
323/// assert_eq!(count.get(), 2);
324///
325/// // ✅ you can create "derived signals" with a Fn() -> T interface
326/// let double_count = move || count.get() * 2; // signals are `Copy` so you can `move` them anywhere
327/// set_count.set(0);
328/// assert_eq!(double_count(), 0);
329/// set_count.set(1);
330/// assert_eq!(double_count(), 2);
331/// # runtime.dispose();
332/// #
333/// ```
334#[cfg_attr(
335    any(debug_assertions, feature="ssr"),
336    instrument(
337        level = "trace",
338        skip_all,
339        fields(
340            ty = %std::any::type_name::<T>()
341        )
342    )
343)]
344#[track_caller]
345pub fn create_signal<T>(value: T) -> (ReadSignal<T>, WriteSignal<T>) {
346    Runtime::current().create_signal(value)
347}
348
349/// Creates a signal that always contains the most recent value emitted by a
350/// [`Stream`](futures::stream::Stream).
351/// If the stream has not yet emitted a value since the signal was created, the signal's
352/// value will be `None`.
353///
354/// **Note**: If used on the server side during server rendering, this will return `None`
355/// immediately and not begin driving the stream.
356#[cfg_attr(
357    any(debug_assertions, feature = "ssr"),
358    instrument(level = "trace", skip_all,)
359)]
360pub fn create_signal_from_stream<T>(
361    #[allow(unused_mut)] // allowed because needed for SSR
362    mut stream: impl Stream<Item = T> + Unpin + 'static,
363) -> ReadSignal<Option<T>> {
364    cfg_if::cfg_if! {
365        if #[cfg(feature = "ssr")] {
366            _ = stream;
367            let (read, _) = create_signal(None);
368            read
369        } else {
370            use crate::spawn_local;
371            use futures::StreamExt;
372
373            let (read, write) = create_signal(None);
374            spawn_local(async move {
375                while let Some(value) = stream.next().await {
376                    write.set(Some(value));
377                }
378            });
379            read
380        }
381    }
382}
383
384/// The getter for a reactive signal.
385///
386/// A signal is a piece of data that may change over time,
387/// and notifies other code when it has changed. This is the
388/// core primitive of Leptos’s reactive system.
389///
390/// `ReadSignal` is also [`Copy`] and `'static`, so it can very easily moved into closures
391/// or copied structs.
392///
393/// ## Core Trait Implementations
394/// - [`.get()`](#impl-SignalGet<T>-for-ReadSignal<T>) (or calling the signal as a function) clones the current
395///   value of the signal. If you call it within an effect, it will cause that effect
396///   to subscribe to the signal, and to re-run whenever the value of the signal changes.
397/// - [`.get_untracked()`](#impl-SignalGetUntracked<T>-for-ReadSignal<T>) clones the value of the signal
398///   without reactively tracking it.
399/// - [`.with()`](#impl-SignalWith<T>-for-ReadSignal<T>) allows you to reactively access the signal’s value without
400///   cloning by applying a callback function.
401/// - [`.with_untracked()`](#impl-SignalWithUntracked<T>-for-ReadSignal<T>) allows you to access the signal’s
402///   value without reactively tracking it.
403/// - [`.to_stream()`](#impl-SignalStream<T>-for-ReadSignal<T>) converts the signal to an `async` stream of values.
404///
405/// # Examples
406/// ```
407/// # use leptos_reactive::*;
408/// # let runtime = create_runtime();
409/// let (count, set_count) = create_signal(0);
410///
411/// // ✅ calling the getter clones and returns the value
412/// assert_eq!(count.get(), 0);
413///
414/// // ✅ calling the setter sets the value
415/// set_count.set(1); // `set_count(1)` on nightly
416/// assert_eq!(count.get(), 1);
417///
418/// // ❌ you could call the getter within the setter
419/// // set_count.set(count.get() + 1);
420///
421/// // ✅ however it's more efficient to use .update() and mutate the value in place
422/// set_count.update(|count: &mut i32| *count += 1);
423/// assert_eq!(count.get(), 2);
424///
425/// // ✅ you can create "derived signals" with the same Fn() -> T interface
426/// let double_count = move || count.get() * 2; // signals are `Copy` so you can `move` them anywhere
427/// set_count.set(0);
428/// assert_eq!(double_count(), 0);
429/// set_count.set(1);
430/// assert_eq!(double_count(), 2);
431/// # runtime.dispose();
432/// #
433/// ```
434pub struct ReadSignal<T>
435where
436    T: 'static,
437{
438    pub(crate) id: NodeId,
439    pub(crate) ty: PhantomData<T>,
440    #[cfg(any(debug_assertions, feature = "ssr"))]
441    pub(crate) defined_at: &'static std::panic::Location<'static>,
442}
443
444impl<T: Clone> SignalGetUntracked for ReadSignal<T> {
445    type Value = T;
446
447    #[cfg_attr(
448        any(debug_assertions, feature = "ssr"),
449        instrument(
450            level = "trace",
451            name = "ReadSignal::get_untracked()",
452            skip_all,
453            fields(
454                id = ?self.id,
455                defined_at = %self.defined_at,
456                ty = %std::any::type_name::<T>()
457            )
458        )
459    )]
460    fn get_untracked(&self) -> T {
461        match with_runtime(|runtime| {
462            self.id.try_with_no_subscription(runtime, T::clone)
463        })
464        .expect("runtime to be alive")
465        {
466            Ok(t) => t,
467            Err(_) => panic_getting_dead_signal(
468                #[cfg(any(debug_assertions, feature = "ssr"))]
469                self.defined_at,
470            ),
471        }
472    }
473
474    #[cfg_attr(
475        any(debug_assertions, feature = "ssr"),
476        instrument(
477            level = "trace",
478            name = "ReadSignal::try_get_untracked()",
479            skip_all,
480            fields(
481                id = ?self.id,
482                defined_at = %self.defined_at,
483                ty = %std::any::type_name::<T>()
484            )
485        )
486    )]
487    #[track_caller]
488    fn try_get_untracked(&self) -> Option<T> {
489        with_runtime(|runtime| {
490            self.id.try_with_no_subscription(runtime, Clone::clone).ok()
491        })
492        .ok()
493        .flatten()
494    }
495}
496
497impl<T> SignalWithUntracked for ReadSignal<T> {
498    type Value = T;
499
500    #[cfg_attr(
501        any(debug_assertions, feature = "ssr"),
502        instrument(
503            level = "trace",
504            name = "ReadSignal::with_untracked()",
505            skip_all,
506            fields(
507                id = ?self.id,
508                defined_at = %self.defined_at,
509                ty = %std::any::type_name::<T>()
510            )
511        )
512    )]
513    #[inline(always)]
514    fn with_untracked<O>(&self, f: impl FnOnce(&T) -> O) -> O {
515        self.with_no_subscription(f)
516    }
517
518    #[cfg_attr(
519        any(debug_assertions, feature = "ssr"),
520        instrument(
521            level = "trace",
522            name = "ReadSignal::try_with_untracked()",
523            skip_all,
524            fields(
525                id = ?self.id,
526                defined_at = %self.defined_at,
527                ty = %std::any::type_name::<T>()
528            )
529        )
530    )]
531    #[track_caller]
532    #[inline(always)]
533    fn try_with_untracked<O>(&self, f: impl FnOnce(&T) -> O) -> Option<O> {
534        match with_runtime(|runtime| {
535            self.id.try_with_no_subscription(runtime, f)
536        }) {
537            Ok(Ok(o)) => Some(o),
538            _ => None,
539        }
540    }
541}
542
543/// # Examples
544///
545/// ```
546/// # use leptos_reactive::*;
547/// # let runtime = create_runtime();
548/// let (name, set_name) = create_signal("Alice".to_string());
549///
550/// // ❌ unnecessarily clones the string
551/// let first_char = move || name.get().chars().next().unwrap();
552/// assert_eq!(first_char(), 'A');
553///
554/// // ✅ gets the first char without cloning the `String`
555/// let first_char = move || name.with(|n| n.chars().next().unwrap());
556/// assert_eq!(first_char(), 'A');
557/// set_name.set("Bob".to_string());
558/// assert_eq!(first_char(), 'B');
559/// # runtime.dispose();
560/// ```
561impl<T> SignalWith for ReadSignal<T> {
562    type Value = T;
563
564    #[cfg_attr(
565        any(debug_assertions, feature = "ssr"),
566        instrument(
567            level = "trace",
568            name = "ReadSignal::with()",
569            skip_all,
570            fields(
571                id = ?self.id,
572                defined_at = %self.defined_at,
573                ty = %std::any::type_name::<T>()
574            )
575        )
576    )]
577    #[track_caller]
578    #[inline(always)]
579    fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
580        let diagnostics = diagnostics!(self);
581
582        match with_runtime(|runtime| self.id.try_with(runtime, f, diagnostics))
583            .expect("runtime to be alive")
584        {
585            Ok(o) => o,
586            Err(_) => panic_getting_dead_signal(
587                #[cfg(any(debug_assertions, feature = "ssr"))]
588                self.defined_at,
589            ),
590        }
591    }
592
593    #[cfg_attr(
594        any(debug_assertions, feature = "ssr"),
595        instrument(
596            level = "trace",
597            name = "ReadSignal::try_with()",
598            skip_all,
599            fields(
600                id = ?self.id,
601                defined_at = %self.defined_at,
602                ty = %std::any::type_name::<T>()
603            )
604        )
605    )]
606    #[track_caller]
607    #[inline(always)]
608    fn try_with<O>(&self, f: impl FnOnce(&T) -> O) -> Option<O> {
609        let diagnostics = diagnostics!(self);
610
611        with_runtime(|runtime| self.id.try_with(runtime, f, diagnostics).ok())
612            .ok()
613            .flatten()
614    }
615}
616
617/// # Examples
618///
619/// ```
620/// # use leptos_reactive::*;
621/// # let runtime = create_runtime();
622/// let (count, set_count) = create_signal(0);
623///
624/// assert_eq!(count.get(), 0);
625///
626/// // count() is shorthand for count.get() on `nightly`
627/// // assert_eq!(count.get(), 0);
628/// # runtime.dispose();
629/// ```
630impl<T: Clone> SignalGet for ReadSignal<T> {
631    type Value = T;
632
633    #[cfg_attr(
634        any(debug_assertions, feature = "ssr"),
635        instrument(
636            level = "trace",
637            name = "ReadSignal::get()",
638            skip_all,
639            fields(
640                id = ?self.id,
641                defined_at = %self.defined_at,
642                ty = %std::any::type_name::<T>()
643            )
644        )
645    )]
646    #[track_caller]
647    fn get(&self) -> T {
648        let diagnostics = diagnostics!(self);
649
650        match with_runtime(|runtime| {
651            self.id.try_with(runtime, T::clone, diagnostics)
652        })
653        .expect("runtime to be alive")
654        {
655            Ok(t) => t,
656            Err(_) => panic_getting_dead_signal(
657                #[cfg(any(debug_assertions, feature = "ssr"))]
658                self.defined_at,
659            ),
660        }
661    }
662
663    #[cfg_attr(
664        any(debug_assertions, feature = "ssr"),
665        instrument(
666            level = "trace",
667            name = "ReadSignal::try_get()",
668            skip_all,
669            fields(
670                id = ?self.id,
671                defined_at = %self.defined_at,
672                ty = %std::any::type_name::<T>()
673            )
674        )
675    )]
676    fn try_get(&self) -> Option<T> {
677        self.try_with(Clone::clone).ok()
678    }
679}
680
681impl<T: Clone> SignalStream<T> for ReadSignal<T> {
682    #[cfg_attr(
683        any(debug_assertions, feature = "ssr"),
684        instrument(
685            level = "trace",
686            name = "ReadSignal::to_stream()",
687            skip_all,
688            fields(
689                id = ?self.id,
690                defined_at = %self.defined_at,
691                ty = %std::any::type_name::<T>()
692            )
693        )
694    )]
695    fn to_stream(&self) -> Pin<Box<dyn Stream<Item = T>>> {
696        let (tx, rx) = futures::channel::mpsc::unbounded();
697
698        let close_channel = tx.clone();
699
700        on_cleanup(move || close_channel.close_channel());
701
702        let this = *self;
703
704        create_isomorphic_effect(move |_| {
705            let _ = tx.unbounded_send(this.get());
706        });
707
708        Box::pin(rx)
709    }
710}
711
712impl<T> SignalDispose for ReadSignal<T> {
713    fn dispose(self) {
714        _ = with_runtime(|runtime| runtime.dispose_node(self.id));
715    }
716}
717
718impl<T> ReadSignal<T>
719where
720    T: 'static,
721{
722    #[track_caller]
723    #[inline(always)]
724    pub(crate) fn with_no_subscription<U>(&self, f: impl FnOnce(&T) -> U) -> U {
725        #[cfg(debug_assertions)]
726        let caller = std::panic::Location::caller();
727
728        self.id
729            .try_with_no_subscription_by_id(f)
730            .unwrap_or_else(|_| {
731                #[cfg(not(debug_assertions))]
732                {
733                    panic!("tried to access ReadSignal that has been disposed")
734                }
735                #[cfg(debug_assertions)]
736                {
737                    panic!(
738                        "at {}, tried to access ReadSignal<{}> defined at {}, \
739                         but it has already been disposed",
740                        caller,
741                        std::any::type_name::<T>(),
742                        self.defined_at
743                    )
744                }
745            })
746    }
747
748    /// Applies the function to the current Signal, if it exists, and subscribes
749    /// the running effect.
750    #[track_caller]
751    #[inline(always)]
752    pub(crate) fn try_with<U>(
753        &self,
754        f: impl FnOnce(&T) -> U,
755    ) -> Result<U, SignalError> {
756        let diagnostics = diagnostics!(self);
757
758        match with_runtime(|runtime| self.id.try_with(runtime, f, diagnostics))
759        {
760            Ok(Ok(v)) => Ok(v),
761            Ok(Err(e)) => Err(e),
762            Err(_) => Err(SignalError::RuntimeDisposed),
763        }
764    }
765}
766
767impl<T> Clone for ReadSignal<T> {
768    fn clone(&self) -> Self {
769        *self
770    }
771}
772
773impl<T> Copy for ReadSignal<T> {}
774
775impl<T> fmt::Debug for ReadSignal<T> {
776    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
777        let mut s = f.debug_struct("ReadSignal");
778        s.field("id", &self.id);
779        s.field("ty", &self.ty);
780        #[cfg(any(debug_assertions, feature = "ssr"))]
781        s.field("defined_at", &self.defined_at);
782        s.finish()
783    }
784}
785
786impl<T> Eq for ReadSignal<T> {}
787
788impl<T> PartialEq for ReadSignal<T> {
789    fn eq(&self, other: &Self) -> bool {
790        self.id == other.id
791    }
792}
793
794impl<T> Hash for ReadSignal<T> {
795    fn hash<H: Hasher>(&self, state: &mut H) {
796        Runtime::current().hash(state);
797        self.id.hash(state);
798    }
799}
800
801/// The setter for a reactive signal.
802///
803/// A signal is a piece of data that may change over time,
804/// and notifies other code when it has changed. This is the
805/// core primitive of Leptos’s reactive system.
806///
807/// Calling [`WriteSignal::update`] will mutate the signal’s value in place,
808/// and notify all subscribers that the signal’s value has changed.
809///
810/// `WriteSignal` implements [`Fn`], such that `set_value(new_value)` is equivalent to
811/// `set_value.update(|value| *value = new_value)`.
812///
813/// `WriteSignal` is [`Copy`] and `'static`, so it can very easily moved into closures
814/// or copied structs.
815///
816/// ## Core Trait Implementations
817/// - [`.set()`](#impl-SignalSet<T>-for-WriteSignal<T>) (or calling the setter as a function)
818///   sets the signal’s value, and notifies all subscribers that the signal’s value has changed.
819///   to subscribe to the signal, and to re-run whenever the value of the signal changes.
820/// - [`.set_untracked()`](#impl-SignalSetUntracked<T>-for-WriteSignal<T>) sets the signal’s value
821///   without notifying its subscribers.
822/// - [`.update()`](#impl-SignalUpdate<T>-for-WriteSignal<T>) mutates the signal’s value in place
823///   and notifies all subscribers that the signal’s value has changed.
824/// - [`.update_untracked()`](#impl-SignalUpdateUntracked<T>-for-WriteSignal<T>) mutates the signal’s value
825///   in place without notifying its subscribers.
826///
827/// ## Examples
828/// ```
829/// # use leptos_reactive::*;
830/// # let runtime = create_runtime();
831/// let (count, set_count) = create_signal(0);
832///
833/// // ✅ calling the setter sets the value
834/// //    `set_count(1)` on nightly
835/// set_count.set(1);
836/// assert_eq!(count.get(), 1);
837///
838/// // ❌ you could call the getter within the setter
839/// // set_count.set(count.get() + 1);
840///
841/// // ✅ however it's more efficient to use .update() and mutate the value in place
842/// set_count.update(|count: &mut i32| *count += 1);
843/// assert_eq!(count.get(), 2);
844/// # runtime.dispose();
845/// #
846/// ```
847pub struct WriteSignal<T>
848where
849    T: 'static,
850{
851    pub(crate) id: NodeId,
852    pub(crate) ty: PhantomData<T>,
853    #[cfg(any(debug_assertions, feature = "ssr"))]
854    pub(crate) defined_at: &'static std::panic::Location<'static>,
855}
856
857impl<T> SignalSetUntracked<T> for WriteSignal<T>
858where
859    T: 'static,
860{
861    #[cfg_attr(
862        any(debug_assertions, feature = "ssr"),
863        instrument(
864            level = "trace",
865            name = "WriteSignal::set_untracked()",
866            skip_all,
867            fields(
868                id = ?self.id,
869                defined_at = %self.defined_at,
870                ty = %std::any::type_name::<T>()
871            )
872        )
873    )]
874    fn set_untracked(&self, new_value: T) {
875        self.id.update_with_no_effect(
876            |v| *v = new_value,
877            #[cfg(debug_assertions)]
878            Some(self.defined_at),
879        );
880    }
881
882    #[cfg_attr(
883        any(debug_assertions, feature = "ssr"),
884        instrument(
885            level = "trace",
886            name = "WriteSignal::try_set_untracked()",
887            skip_all,
888            fields(
889                id = ?self.id,
890                defined_at = %self.defined_at,
891                ty = %std::any::type_name::<T>()
892            )
893        )
894    )]
895    fn try_set_untracked(&self, new_value: T) -> Option<T> {
896        let mut new_value = Some(new_value);
897
898        self.id.update(
899            |t| *t = new_value.take().unwrap(),
900            #[cfg(debug_assertions)]
901            None,
902        );
903
904        new_value
905    }
906}
907
908impl<T> SignalUpdateUntracked<T> for WriteSignal<T> {
909    #[cfg_attr(
910        any(debug_assertions, feature = "ssr"),
911        instrument(
912            level = "trace",
913            name = "WriteSignal::updated_untracked()",
914            skip_all,
915            fields(
916                id = ?self.id,
917                defined_at = %self.defined_at,
918                ty = %std::any::type_name::<T>()
919            )
920        )
921    )]
922    #[inline(always)]
923    fn update_untracked(&self, f: impl FnOnce(&mut T)) {
924        self.id.update_with_no_effect(
925            f,
926            #[cfg(debug_assertions)]
927            Some(self.defined_at),
928        );
929    }
930
931    #[inline(always)]
932    fn try_update_untracked<O>(
933        &self,
934        f: impl FnOnce(&mut T) -> O,
935    ) -> Option<O> {
936        self.id.update_with_no_effect(
937            f,
938            #[cfg(debug_assertions)]
939            None,
940        )
941    }
942}
943
944/// # Examples
945/// ```
946/// # use leptos_reactive::*;
947/// # let runtime = create_runtime();
948/// let (count, set_count) = create_signal(0);
949///
950/// // notifies subscribers
951/// set_count.update(|n| *n = 1); // it's easier just to call set_count.set(1), though!
952/// assert_eq!(count.get(), 1);
953///
954/// // you can include arbitrary logic in this update function
955/// // also notifies subscribers, even though the value hasn't changed
956/// set_count.update(|n| if *n > 3 { *n += 1 });
957/// assert_eq!(count.get(), 1);
958/// # runtime.dispose();
959/// ```
960impl<T> SignalUpdate for WriteSignal<T> {
961    type Value = T;
962
963    #[cfg_attr(
964        any(debug_assertions, feature = "ssr"),
965        instrument(
966            name = "WriteSignal::update()",
967            level = "trace",
968            skip_all,
969            fields(
970                id = ?self.id,
971                defined_at = %self.defined_at,
972                ty = %std::any::type_name::<T>()
973            )
974        )
975    )]
976    #[inline(always)]
977    fn update(&self, f: impl FnOnce(&mut T)) {
978        if self
979            .id
980            .update(
981                f,
982                #[cfg(debug_assertions)]
983                Some(self.defined_at),
984            )
985            .is_none()
986        {
987            warn_updating_dead_signal(
988                #[cfg(any(debug_assertions, feature = "ssr"))]
989                self.defined_at,
990            );
991        }
992    }
993
994    #[cfg_attr(
995        any(debug_assertions, feature = "ssr"),
996        instrument(
997            name = "WriteSignal::try_update()",
998            level = "trace",
999            skip_all,
1000            fields(
1001                id = ?self.id,
1002                defined_at = %self.defined_at,
1003                ty = %std::any::type_name::<T>()
1004            )
1005        )
1006    )]
1007    #[inline(always)]
1008    fn try_update<O>(&self, f: impl FnOnce(&mut T) -> O) -> Option<O> {
1009        self.id.update(
1010            f,
1011            #[cfg(debug_assertions)]
1012            None,
1013        )
1014    }
1015}
1016
1017/// # Examples
1018///
1019/// ```
1020/// # use leptos_reactive::*;
1021/// # let runtime = create_runtime();
1022/// let (count, set_count) = create_signal(0);
1023///
1024/// // notifies subscribers
1025/// set_count.update(|n| *n = 1); // it's easier just to call set_count.set(1), though!
1026/// assert_eq!(count.get(), 1);
1027///
1028/// // you can include arbitrary logic in this update function
1029/// // also notifies subscribers, even though the value hasn't changed
1030/// set_count.update(|n| if *n > 3 { *n += 1 });
1031/// assert_eq!(count.get(), 1);
1032/// # runtime.dispose();
1033/// ```
1034impl<T> SignalSet for WriteSignal<T> {
1035    type Value = T;
1036
1037    #[cfg_attr(
1038        any(debug_assertions, feature = "ssr"),
1039        instrument(
1040            level = "trace",
1041            name = "WriteSignal::set()",
1042            skip_all,
1043            fields(
1044                id = ?self.id,
1045                defined_at = %self.defined_at,
1046                ty = %std::any::type_name::<T>()
1047            )
1048        )
1049    )]
1050    fn set(&self, new_value: T) {
1051        self.id.update(
1052            |n| *n = new_value,
1053            #[cfg(debug_assertions)]
1054            Some(self.defined_at),
1055        );
1056    }
1057
1058    #[cfg_attr(
1059        any(debug_assertions, feature = "ssr"),
1060        instrument(
1061            level = "trace",
1062            name = "WriteSignal::try_set()",
1063            skip_all,
1064            fields(
1065                id = ?self.id,
1066                defined_at = %self.defined_at,
1067                ty = %std::any::type_name::<T>()
1068            )
1069        )
1070    )]
1071    fn try_set(&self, new_value: T) -> Option<T> {
1072        let mut new_value = Some(new_value);
1073
1074        self.id.update(
1075            |t| *t = new_value.take().unwrap(),
1076            #[cfg(debug_assertions)]
1077            None,
1078        );
1079
1080        new_value
1081    }
1082}
1083
1084impl<T> SignalDispose for WriteSignal<T> {
1085    fn dispose(self) {
1086        _ = with_runtime(|runtime| runtime.dispose_node(self.id));
1087    }
1088}
1089
1090impl<T> Clone for WriteSignal<T> {
1091    fn clone(&self) -> Self {
1092        *self
1093    }
1094}
1095
1096impl<T> Copy for WriteSignal<T> {}
1097
1098impl<T> fmt::Debug for WriteSignal<T> {
1099    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1100        let mut s = f.debug_struct("WriteSignal");
1101        s.field("id", &self.id);
1102        s.field("ty", &self.ty);
1103        #[cfg(any(debug_assertions, feature = "ssr"))]
1104        s.field("defined_at", &self.defined_at);
1105        s.finish()
1106    }
1107}
1108
1109impl<T> Eq for WriteSignal<T> {}
1110
1111impl<T> PartialEq for WriteSignal<T> {
1112    fn eq(&self, other: &Self) -> bool {
1113        self.id == other.id
1114    }
1115}
1116
1117impl<T> Hash for WriteSignal<T> {
1118    fn hash<H: Hasher>(&self, state: &mut H) {
1119        Runtime::current().hash(state);
1120        self.id.hash(state);
1121    }
1122}
1123
1124/// Creates a reactive signal with the getter and setter unified in one value.
1125/// You may prefer this style, or it may be easier to pass around in a context
1126/// or as a function argument.
1127/// ```
1128/// # use leptos_reactive::*;
1129/// # let runtime = create_runtime();
1130/// let count = create_rw_signal(0);
1131///
1132/// // ✅ set the value
1133/// count.set(1);
1134/// assert_eq!(count.get(), 1);
1135///
1136/// // ❌ you can call the getter within the setter
1137/// // count.set(count.get() + 1);
1138///
1139/// // ✅ however, it's more efficient to use .update() and mutate the value in place
1140/// count.update(|count: &mut i32| *count += 1);
1141/// assert_eq!(count.get(), 2);
1142/// # runtime.dispose();
1143/// #
1144/// ```
1145#[cfg_attr(
1146 any(debug_assertions, feature="ssr"),
1147    instrument(
1148        level = "trace",
1149        skip_all,
1150        fields(
1151            ty = %std::any::type_name::<T>()
1152        )
1153    )
1154)]
1155#[track_caller]
1156pub fn create_rw_signal<T>(value: T) -> RwSignal<T> {
1157    Runtime::current().create_rw_signal(value)
1158}
1159
1160/// A signal that combines the getter and setter into one value, rather than
1161/// separating them into a [`ReadSignal`] and a [`WriteSignal`]. You may prefer this
1162/// its style, or it may be easier to pass around in a context or as a function argument.
1163///
1164/// ## Core Trait Implementations
1165/// - [`.get()`](#impl-SignalGet<T>-for-RwSignal<T>) clones the current
1166///   value of the signal. If you call it within an effect, it will cause that effect
1167///   to subscribe to the signal, and to re-run whenever the value of the signal changes.
1168/// - [`.get_untracked()`](#impl-SignalGetUntracked<T>-for-RwSignal<T>) clones the value of the signal
1169///   without reactively tracking it.
1170/// - [`.with()`](#impl-SignalWith<T>-for-RwSignal<T>) allows you to reactively access the signal’s value without
1171///   cloning by applying a callback function.
1172/// - [`.with_untracked()`](#impl-SignalWithUntracked<T>-for-RwSignal<T>) allows you to access the signal’s
1173///   value without reactively tracking it.
1174/// - [`.set()`](#impl-SignalSet<T>-for-RwSignal<T>) sets the signal’s value,
1175///   and notifies all subscribers that the signal’s value has changed.
1176///   to subscribe to the signal, and to re-run whenever the value of the signal changes.
1177/// - [`.set_untracked()`](#impl-SignalSetUntracked<T>-for-RwSignal<T>) sets the signal’s value
1178///   without notifying its subscribers.
1179/// - [`.update()`](#impl-SignalUpdate<T>-for-RwSignal<T>) mutates the signal’s value in place
1180///   and notifies all subscribers that the signal’s value has changed.
1181/// - [`.update_untracked()`](#impl-SignalUpdateUntracked<T>-for-RwSignal<T>) mutates the signal’s value
1182///   in place without notifying its subscribers.
1183/// - [`.to_stream()`](#impl-SignalStream<T>-for-RwSignal<T>) converts the signal to an `async` stream of values.
1184///
1185/// ```
1186/// # use leptos_reactive::*;
1187/// # let runtime = create_runtime();
1188/// let count = create_rw_signal(0);
1189///
1190/// // ✅ set the value
1191/// count.set(1);
1192/// assert_eq!(count.get(), 1);
1193///
1194/// // ❌ you can call the getter within the setter
1195/// // count.set(count.get() + 1);
1196///
1197/// // ✅ however, it's more efficient to use .update() and mutate the value in place
1198/// count.update(|count: &mut i32| *count += 1);
1199/// assert_eq!(count.get(), 2);
1200/// # runtime.dispose();
1201/// #
1202/// ```
1203pub struct RwSignal<T>
1204where
1205    T: 'static,
1206{
1207    pub(crate) id: NodeId,
1208    pub(crate) ty: PhantomData<T>,
1209    #[cfg(any(debug_assertions, feature = "ssr"))]
1210    pub(crate) defined_at: &'static std::panic::Location<'static>,
1211}
1212
1213impl<T: Default> Default for RwSignal<T> {
1214    fn default() -> Self {
1215        Self::new(Default::default())
1216    }
1217}
1218
1219impl<T> Clone for RwSignal<T> {
1220    fn clone(&self) -> Self {
1221        *self
1222    }
1223}
1224
1225impl<T> Copy for RwSignal<T> {}
1226
1227impl<T> fmt::Debug for RwSignal<T> {
1228    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1229        let mut s = f.debug_struct("RwSignal");
1230        s.field("id", &self.id);
1231        s.field("ty", &self.ty);
1232        #[cfg(any(debug_assertions, feature = "ssr"))]
1233        s.field("defined_at", &self.defined_at);
1234        s.finish()
1235    }
1236}
1237
1238impl<T> Eq for RwSignal<T> {}
1239
1240impl<T> PartialEq for RwSignal<T> {
1241    fn eq(&self, other: &Self) -> bool {
1242        self.id == other.id
1243    }
1244}
1245
1246impl<T> Hash for RwSignal<T> {
1247    fn hash<H: Hasher>(&self, state: &mut H) {
1248        Runtime::current().hash(state);
1249        self.id.hash(state);
1250    }
1251}
1252
1253impl<T> From<T> for RwSignal<T> {
1254    fn from(value: T) -> Self {
1255        create_rw_signal(value)
1256    }
1257}
1258
1259impl<T: Clone> SignalGetUntracked for RwSignal<T> {
1260    type Value = T;
1261
1262    #[cfg_attr(
1263        any(debug_assertions, feature = "ssr"),
1264        instrument(
1265            level = "trace",
1266            name = "RwSignal::get_untracked()",
1267            skip_all,
1268            fields(
1269                id = ?self.id,
1270                defined_at = %self.defined_at,
1271                ty = %std::any::type_name::<T>()
1272            )
1273        )
1274    )]
1275    #[track_caller]
1276    fn get_untracked(&self) -> T {
1277        #[cfg(debug_assertions)]
1278        let caller = std::panic::Location::caller();
1279
1280        self.id
1281            .try_with_no_subscription_by_id(Clone::clone)
1282            .unwrap_or_else(|_| {
1283                #[cfg(not(debug_assertions))]
1284                {
1285                    panic!("tried to access RwSignal that has been disposed")
1286                }
1287                #[cfg(debug_assertions)]
1288                {
1289                    panic!(
1290                        "at {}, tried to access RwSignal<{}> defined at {}, \
1291                         but it has already been disposed",
1292                        caller,
1293                        std::any::type_name::<T>(),
1294                        self.defined_at
1295                    )
1296                }
1297            })
1298    }
1299
1300    #[cfg_attr(
1301        any(debug_assertions, feature = "ssr"),
1302        instrument(
1303            level = "trace",
1304            name = "RwSignal::try_get_untracked()",
1305            skip_all,
1306            fields(
1307                id = ?self.id,
1308                defined_at = %self.defined_at,
1309                ty = %std::any::type_name::<T>()
1310            )
1311        )
1312    )]
1313    #[track_caller]
1314    fn try_get_untracked(&self) -> Option<T> {
1315        with_runtime(|runtime| {
1316            self.id.try_with_no_subscription(runtime, Clone::clone).ok()
1317        })
1318        .ok()
1319        .flatten()
1320    }
1321}
1322
1323impl<T> SignalWithUntracked for RwSignal<T> {
1324    type Value = T;
1325
1326    #[cfg_attr(
1327        any(debug_assertions, feature = "ssr"),
1328        instrument(
1329            level = "trace",
1330            name = "RwSignal::with_untracked()",
1331            skip_all,
1332            fields(
1333                id = ?self.id,
1334                defined_at = %self.defined_at,
1335                ty = %std::any::type_name::<T>()
1336            )
1337        )
1338    )]
1339    #[inline(always)]
1340    fn with_untracked<O>(&self, f: impl FnOnce(&T) -> O) -> O {
1341        self.id
1342            .try_with_no_subscription_by_id(f)
1343            .unwrap_or_else(|_| {
1344                #[cfg(not(debug_assertions))]
1345                {
1346                    panic!("tried to access RwSignal that has been disposed")
1347                }
1348                #[cfg(debug_assertions)]
1349                {
1350                    panic!(
1351                        "tried to access RwSignal<{}> defined at {}, but it \
1352                         has already been disposed",
1353                        std::any::type_name::<T>(),
1354                        self.defined_at
1355                    )
1356                }
1357            })
1358    }
1359
1360    #[cfg_attr(
1361        any(debug_assertions, feature = "ssr"),
1362        instrument(
1363            level = "trace",
1364            name = "RwSignal::try_with_untracked()",
1365            skip_all,
1366            fields(
1367                id = ?self.id,
1368                defined_at = %self.defined_at,
1369                ty = %std::any::type_name::<T>()
1370            )
1371        )
1372    )]
1373    #[track_caller]
1374    #[inline(always)]
1375    fn try_with_untracked<O>(&self, f: impl FnOnce(&T) -> O) -> Option<O> {
1376        match with_runtime(|runtime| {
1377            self.id.try_with_no_subscription(runtime, f)
1378        }) {
1379            Ok(Ok(o)) => Some(o),
1380            _ => None,
1381        }
1382    }
1383}
1384
1385impl<T> SignalSetUntracked<T> for RwSignal<T> {
1386    #[cfg_attr(
1387        any(debug_assertions, feature = "ssr"),
1388        instrument(
1389            level = "trace",
1390            name = "RwSignal::set_untracked()",
1391            skip_all,
1392            fields(
1393                id = ?self.id,
1394                defined_at = %self.defined_at,
1395                ty = %std::any::type_name::<T>()
1396            )
1397        )
1398    )]
1399    fn set_untracked(&self, new_value: T) {
1400        self.id.update_with_no_effect(
1401            |v| *v = new_value,
1402            #[cfg(debug_assertions)]
1403            Some(self.defined_at),
1404        );
1405    }
1406
1407    #[cfg_attr(
1408        any(debug_assertions, feature = "ssr"),
1409        instrument(
1410            level = "trace",
1411            name = "RwSignal::try_set_untracked()",
1412            skip_all,
1413            fields(
1414                id = ?self.id,
1415                defined_at = %self.defined_at,
1416                ty = %std::any::type_name::<T>()
1417            )
1418        )
1419    )]
1420    fn try_set_untracked(&self, new_value: T) -> Option<T> {
1421        let mut new_value = Some(new_value);
1422
1423        self.id.update(
1424            |t| *t = new_value.take().unwrap(),
1425            #[cfg(debug_assertions)]
1426            None,
1427        );
1428
1429        new_value
1430    }
1431}
1432
1433impl<T> SignalUpdateUntracked<T> for RwSignal<T> {
1434    #[cfg_attr(
1435        any(debug_assertions, feature="ssr"),
1436        instrument(
1437            level = "trace",
1438            name = "RwSignal::update_untracked()",
1439            skip_all,
1440            fields(
1441                id = ?self.id,
1442                defined_at = %self.defined_at,
1443                ty = %std::any::type_name::<T>()
1444            )
1445        )
1446    )]
1447    #[inline(always)]
1448    fn update_untracked(&self, f: impl FnOnce(&mut T)) {
1449        self.id.update_with_no_effect(
1450            f,
1451            #[cfg(debug_assertions)]
1452            Some(self.defined_at),
1453        );
1454    }
1455
1456    #[cfg_attr(
1457        any(debug_assertions, feature = "ssr"),
1458        instrument(
1459            level = "trace",
1460            name = "RwSignal::try_update_untracked()",
1461            skip_all,
1462            fields(
1463                id = ?self.id,
1464                defined_at = %self.defined_at,
1465                ty = %std::any::type_name::<T>()
1466            )
1467        )
1468    )]
1469    #[inline(always)]
1470    fn try_update_untracked<O>(
1471        &self,
1472        f: impl FnOnce(&mut T) -> O,
1473    ) -> Option<O> {
1474        self.id.update_with_no_effect(
1475            f,
1476            #[cfg(debug_assertions)]
1477            None,
1478        )
1479    }
1480}
1481
1482/// # Examples
1483///
1484/// ```
1485/// # use leptos_reactive::*;
1486/// # let runtime = create_runtime();
1487/// let name = create_rw_signal("Alice".to_string());
1488///
1489/// // ❌ unnecessarily clones the string
1490/// let first_char = move || name.get().chars().next().unwrap();
1491/// assert_eq!(first_char(), 'A');
1492///
1493/// // ✅ gets the first char without cloning the `String`
1494/// let first_char = move || name.with(|n| n.chars().next().unwrap());
1495/// assert_eq!(first_char(), 'A');
1496/// name.set("Bob".to_string());
1497/// assert_eq!(first_char(), 'B');
1498/// # runtime.dispose();
1499/// #
1500/// ```
1501impl<T> SignalWith for RwSignal<T> {
1502    type Value = T;
1503
1504    #[cfg_attr(
1505        any(debug_assertions, feature = "ssr"),
1506        instrument(
1507            level = "trace",
1508            name = "RwSignal::with()",
1509            skip_all,
1510            fields(
1511                id = ?self.id,
1512                defined_at = %self.defined_at,
1513                ty = %std::any::type_name::<T>()
1514            )
1515        )
1516    )]
1517    #[track_caller]
1518    #[inline(always)]
1519    fn with<O>(&self, f: impl FnOnce(&T) -> O) -> O {
1520        let diagnostics = diagnostics!(self);
1521
1522        match with_runtime(|runtime| self.id.try_with(runtime, f, diagnostics))
1523            .expect("runtime to be alive")
1524        {
1525            Ok(o) => o,
1526            Err(_) => panic_getting_dead_signal(
1527                #[cfg(any(debug_assertions, feature = "ssr"))]
1528                self.defined_at,
1529            ),
1530        }
1531    }
1532
1533    #[cfg_attr(
1534        any(debug_assertions, feature = "ssr"),
1535        instrument(
1536            level = "trace",
1537            name = "RwSignal::try_with()",
1538            skip_all,
1539            fields(
1540                id = ?self.id,
1541                defined_at = %self.defined_at,
1542                ty = %std::any::type_name::<T>()
1543            )
1544        )
1545    )]
1546    #[track_caller]
1547    #[inline(always)]
1548    fn try_with<O>(&self, f: impl FnOnce(&T) -> O) -> Option<O> {
1549        let diagnostics = diagnostics!(self);
1550
1551        with_runtime(|runtime| self.id.try_with(runtime, f, diagnostics).ok())
1552            .ok()
1553            .flatten()
1554    }
1555}
1556
1557/// # Examples
1558///
1559/// ```
1560/// # use leptos_reactive::*;
1561/// # let runtime = create_runtime();
1562/// let count = create_rw_signal(0);
1563///
1564/// assert_eq!(count.get(), 0);
1565///
1566/// // count() is shorthand for count.get() on `nightly`
1567/// // assert_eq!(count(), 0);
1568/// # runtime.dispose();
1569/// #
1570/// ```
1571impl<T: Clone> SignalGet for RwSignal<T> {
1572    type Value = T;
1573
1574    #[cfg_attr(
1575        any(debug_assertions, feature = "ssr"),
1576        instrument(
1577            level = "trace",
1578            name = "RwSignal::get()",
1579            skip_all,
1580            fields(
1581                id = ?self.id,
1582                defined_at = %self.defined_at,
1583                ty = %std::any::type_name::<T>()
1584            )
1585        )
1586    )]
1587    #[track_caller]
1588    fn get(&self) -> T
1589    where
1590        T: Clone,
1591    {
1592        let diagnostics = diagnostics!(self);
1593
1594        match with_runtime(|runtime| {
1595            self.id.try_with(runtime, T::clone, diagnostics)
1596        })
1597        .expect("runtime to be alive")
1598        {
1599            Ok(t) => t,
1600            Err(_) => panic_getting_dead_signal(
1601                #[cfg(any(debug_assertions, feature = "ssr"))]
1602                self.defined_at,
1603            ),
1604        }
1605    }
1606
1607    #[cfg_attr(
1608        any(debug_assertions, feature = "ssr"),
1609        instrument(
1610            level = "trace",
1611            name = "RwSignal::try_get()",
1612            skip_all,
1613            fields(
1614                id = ?self.id,
1615                defined_at = %self.defined_at,
1616                ty = %std::any::type_name::<T>()
1617            )
1618        )
1619    )]
1620    #[track_caller]
1621    fn try_get(&self) -> Option<T> {
1622        let diagnostics = diagnostics!(self);
1623
1624        with_runtime(|runtime| {
1625            self.id.try_with(runtime, Clone::clone, diagnostics).ok()
1626        })
1627        .ok()
1628        .flatten()
1629    }
1630}
1631
1632/// # Examples
1633///
1634/// ```
1635/// # use leptos_reactive::*;
1636/// # let runtime = create_runtime();
1637/// let count = create_rw_signal(0);
1638///
1639/// // notifies subscribers
1640/// count.update(|n| *n = 1); // it's easier just to call set_count.set(1), though!
1641/// assert_eq!(count.get(), 1);
1642///
1643/// // you can include arbitrary logic in this update function
1644/// // also notifies subscribers, even though the value hasn't changed
1645/// count.update(|n| {
1646///     if *n > 3 {
1647///         *n += 1
1648///     }
1649/// });
1650/// assert_eq!(count.get(), 1);
1651/// # runtime.dispose();
1652/// ```
1653impl<T> SignalUpdate for RwSignal<T> {
1654    type Value = T;
1655
1656    #[cfg_attr(
1657        any(debug_assertions, feature = "ssr"),
1658        instrument(
1659            level = "trace",
1660            name = "RwSignal::update()",
1661            skip_all,
1662            fields(
1663                id = ?self.id,
1664                defined_at = %self.defined_at,
1665                ty = %std::any::type_name::<T>()
1666            )
1667        )
1668    )]
1669    #[inline(always)]
1670    fn update(&self, f: impl FnOnce(&mut T)) {
1671        if self
1672            .id
1673            .update(
1674                f,
1675                #[cfg(debug_assertions)]
1676                Some(self.defined_at),
1677            )
1678            .is_none()
1679        {
1680            warn_updating_dead_signal(
1681                #[cfg(any(debug_assertions, feature = "ssr"))]
1682                self.defined_at,
1683            );
1684        }
1685    }
1686
1687    #[cfg_attr(
1688        any(debug_assertions, feature = "ssr"),
1689        instrument(
1690            level = "trace",
1691            name = "RwSignal::try_update()",
1692            skip_all,
1693            fields(
1694                id = ?self.id,
1695                defined_at = %self.defined_at,
1696                ty = %std::any::type_name::<T>()
1697            )
1698        )
1699    )]
1700    #[inline(always)]
1701    fn try_update<O>(&self, f: impl FnOnce(&mut T) -> O) -> Option<O> {
1702        self.id.update(
1703            f,
1704            #[cfg(debug_assertions)]
1705            None,
1706        )
1707    }
1708}
1709
1710/// # Examples
1711///
1712/// ```
1713/// # use leptos_reactive::*;
1714/// # let runtime = create_runtime();
1715/// let count = create_rw_signal(0);
1716///
1717/// assert_eq!(count.get(), 0);
1718/// count.set(1);
1719/// assert_eq!(count.get(), 1);
1720/// # runtime.dispose();
1721/// ```
1722impl<T> SignalSet for RwSignal<T> {
1723    type Value = T;
1724
1725    #[cfg_attr(
1726        any(debug_assertions, feature = "ssr"),
1727        instrument(
1728            level = "trace",
1729            name = "RwSignal::set()",
1730            skip_all,
1731            fields(
1732                id = ?self.id,
1733                defined_at = %self.defined_at,
1734                ty = %std::any::type_name::<T>()
1735            )
1736        )
1737    )]
1738    fn set(&self, value: T) {
1739        self.id.update(
1740            |n| *n = value,
1741            #[cfg(debug_assertions)]
1742            Some(self.defined_at),
1743        );
1744    }
1745
1746    #[cfg_attr(
1747        any(debug_assertions, feature = "ssr"),
1748        instrument(
1749            level = "trace",
1750            name = "RwSignal::try_set()",
1751            skip_all,
1752            fields(
1753                id = ?self.id,
1754                defined_at = %self.defined_at,
1755                ty = %std::any::type_name::<T>()
1756            )
1757        )
1758    )]
1759    fn try_set(&self, new_value: T) -> Option<T> {
1760        let mut new_value = Some(new_value);
1761
1762        self.id.update(
1763            |t| *t = new_value.take().unwrap(),
1764            #[cfg(debug_assertions)]
1765            None,
1766        );
1767
1768        new_value
1769    }
1770}
1771
1772impl<T: Clone> SignalStream<T> for RwSignal<T> {
1773    fn to_stream(&self) -> Pin<Box<dyn Stream<Item = T>>> {
1774        let (tx, rx) = futures::channel::mpsc::unbounded();
1775
1776        let close_channel = tx.clone();
1777
1778        on_cleanup(move || close_channel.close_channel());
1779
1780        let this = *self;
1781
1782        create_isomorphic_effect(move |_| {
1783            let _ = tx.unbounded_send(this.get());
1784        });
1785
1786        Box::pin(rx)
1787    }
1788}
1789
1790impl<T> SignalDispose for RwSignal<T> {
1791    fn dispose(self) {
1792        _ = with_runtime(|runtime| runtime.dispose_node(self.id));
1793    }
1794}
1795
1796impl<T> RwSignal<T> {
1797    /// Creates a reactive signal with the getter and setter unified in one value.
1798    /// You may prefer this style, or it may be easier to pass around in a context
1799    /// or as a function argument.
1800    ///
1801    /// This is identical to [`create_rw_signal`].
1802    /// ```
1803    /// # use leptos_reactive::*;
1804    /// # let runtime = create_runtime();
1805    /// let count = RwSignal::new(0);
1806    ///
1807    /// // ✅ set the value
1808    /// count.set(1);
1809    /// assert_eq!(count.get(), 1);
1810    ///
1811    /// // ❌ you can call the getter within the setter
1812    /// // count.set(count.get() + 1);
1813    ///
1814    /// // ✅ however, it's more efficient to use .update() and mutate the value in place
1815    /// count.update(|count: &mut i32| *count += 1);
1816    /// assert_eq!(count.get(), 2);
1817    /// # runtime.dispose();
1818    /// #
1819    /// ```
1820    #[inline(always)]
1821    #[track_caller]
1822    pub fn new(value: T) -> Self {
1823        create_rw_signal(value)
1824    }
1825
1826    /// Returns a read-only handle to the signal.
1827    ///
1828    /// Useful if you're trying to give read access to another component but ensure that it can't write
1829    /// to the signal and cause other parts of the DOM to update.
1830    /// ```
1831    /// # use leptos_reactive::*;
1832    /// # let runtime = create_runtime();
1833    /// let count = create_rw_signal(0);
1834    /// let read_count = count.read_only();
1835    /// assert_eq!(count.get(), 0);
1836    /// assert_eq!(read_count.get(), 0);
1837    /// count.set(1);
1838    /// assert_eq!(count.get(), 1);
1839    /// assert_eq!(read_count.get(), 1);
1840    /// # runtime.dispose();
1841    /// ```
1842    #[cfg_attr(
1843        any(debug_assertions, feature = "ssr"),
1844        instrument(
1845            level = "trace",
1846            name = "RwSignal::read_only()",
1847            skip_all,
1848            fields(
1849                id = ?self.id,
1850                defined_at = %self.defined_at,
1851                ty = %std::any::type_name::<T>()
1852            )
1853        )
1854    )]
1855    #[track_caller]
1856    pub fn read_only(&self) -> ReadSignal<T> {
1857        ReadSignal {
1858            id: self.id,
1859            ty: PhantomData,
1860            #[cfg(any(debug_assertions, feature = "ssr"))]
1861            defined_at: std::panic::Location::caller(),
1862        }
1863    }
1864
1865    /// Returns a write-only handle to the signal.
1866    ///
1867    /// Useful if you're trying to give write access to another component, or split an
1868    /// [`RwSignal`] into a [`ReadSignal`] and a [`WriteSignal`].
1869    /// ```
1870    /// # use leptos_reactive::*;
1871    /// # let runtime = create_runtime();
1872    /// let count = create_rw_signal(0);
1873    /// let set_count = count.write_only();
1874    /// assert_eq!(count.get(), 0);
1875    /// set_count.set(1);
1876    /// assert_eq!(count.get(), 1);
1877    /// # runtime.dispose();
1878    /// ```
1879    #[cfg_attr(
1880        any(debug_assertions, feature = "ssr"),
1881        instrument(
1882            level = "trace",
1883            name = "RwSignal::write_only()",
1884            skip_all,
1885            fields(
1886                id = ?self.id,
1887                defined_at = %self.defined_at,
1888                ty = %std::any::type_name::<T>()
1889            )
1890        )
1891    )]
1892    #[track_caller]
1893    pub fn write_only(&self) -> WriteSignal<T> {
1894        WriteSignal {
1895            id: self.id,
1896            ty: PhantomData,
1897            #[cfg(any(debug_assertions, feature = "ssr"))]
1898            defined_at: std::panic::Location::caller(),
1899        }
1900    }
1901
1902    /// Splits an `RwSignal` into its getter and setter.
1903    /// ```
1904    /// # use leptos_reactive::*;
1905    /// # let runtime = create_runtime();
1906    /// let count = create_rw_signal(0);
1907    /// let (get_count, set_count) = count.split();
1908    /// assert_eq!(count.get(), 0);
1909    /// assert_eq!(get_count.get(), 0);
1910    /// set_count.set(1);
1911    /// assert_eq!(count.get(), 1);
1912    /// assert_eq!(get_count.get(), 1);
1913    /// # runtime.dispose();
1914    /// ```
1915    #[cfg_attr(
1916        any(debug_assertions, feature = "ssr"),
1917        instrument(
1918            level = "trace",
1919            name = "RwSignal::split()",
1920            skip_all,
1921            fields(
1922                id = ?self.id,
1923                defined_at = %self.defined_at,
1924                ty = %std::any::type_name::<T>()
1925            )
1926        )
1927    )]
1928    #[track_caller]
1929    pub fn split(&self) -> (ReadSignal<T>, WriteSignal<T>) {
1930        (
1931            ReadSignal {
1932                id: self.id,
1933                ty: PhantomData,
1934                #[cfg(any(debug_assertions, feature = "ssr"))]
1935                defined_at: std::panic::Location::caller(),
1936            },
1937            WriteSignal {
1938                id: self.id,
1939                ty: PhantomData,
1940                #[cfg(any(debug_assertions, feature = "ssr"))]
1941                defined_at: std::panic::Location::caller(),
1942            },
1943        )
1944    }
1945}
1946
1947#[derive(Debug, Error)]
1948pub(crate) enum SignalError {
1949    #[error("tried to access a signal in a runtime that had been disposed")]
1950    RuntimeDisposed,
1951    #[error("tried to access a signal that had been disposed")]
1952    Disposed,
1953    #[error("error casting signal to type {0}")]
1954    Type(&'static str),
1955}
1956
1957impl NodeId {
1958    #[track_caller]
1959    pub(crate) fn subscribe(
1960        &self,
1961        runtime: &Runtime,
1962        #[allow(unused)] diagnostics: AccessDiagnostics,
1963    ) {
1964        // add subscriber
1965        if let Some(observer) = runtime.observer.get() {
1966            // add this observer to this node's dependencies (to allow notification)
1967            let mut subs = runtime.node_subscribers.borrow_mut();
1968            if let Some(subs) = subs.entry(*self) {
1969                subs.or_default().borrow_mut().insert(observer);
1970            }
1971
1972            // add this node to the observer's sources (to allow cleanup)
1973            let mut sources = runtime.node_sources.borrow_mut();
1974            if let Some(sources) = sources.entry(observer) {
1975                let sources = sources.or_default();
1976                sources.borrow_mut().insert(*self);
1977            }
1978        } else {
1979            #[cfg(all(debug_assertions, not(feature = "ssr")))]
1980            {
1981                if !SpecialNonReactiveZone::is_inside() {
1982                    let AccessDiagnostics {
1983                        called_at,
1984                        defined_at,
1985                    } = diagnostics;
1986                    crate::macros::debug_warn!(
1987                        "At {called_at}, you access a signal or memo (defined \
1988                         at {defined_at}) outside a reactive tracking \
1989                         context. This might mean your app is not responding \
1990                         to changes in signal values in the way you \
1991                         expect.\n\nHere’s how to fix it:\n\n1. If this is \
1992                         inside a `view!` macro, make sure you are passing a \
1993                         function, not a value.\n  ❌ NO  <p>{{x.get() * \
1994                         2}}</p>\n  ✅ YES <p>{{move || x.get() * \
1995                         2}}</p>\n\n2. If it’s in the body of a component, \
1996                         try wrapping this access in a closure: \n  ❌ NO  \
1997                         let y = x.get() * 2\n  ✅ YES let y = move || \
1998                         x.get() * 2.\n\n3. If you’re *trying* to access the \
1999                         value without tracking, use `.get_untracked()` or \
2000                         `.with_untracked()` instead."
2001                    );
2002                }
2003            }
2004        }
2005    }
2006
2007    fn try_with_no_subscription_inner(
2008        &self,
2009        runtime: &Runtime,
2010    ) -> Result<Rc<RefCell<dyn Any>>, SignalError> {
2011        runtime.update_if_necessary(*self);
2012        let nodes = runtime.nodes.borrow();
2013        let node = nodes.get(*self).ok_or(SignalError::Disposed)?;
2014        Ok(node.value())
2015    }
2016
2017    #[inline(always)]
2018    pub(crate) fn try_with_no_subscription_by_id<T, U>(
2019        &self,
2020        f: impl FnOnce(&T) -> U,
2021    ) -> Result<U, SignalError>
2022    where
2023        T: 'static,
2024    {
2025        with_runtime(|runtime| self.try_with_no_subscription(runtime, f))
2026            .expect("runtime to be alive")
2027    }
2028
2029    #[track_caller]
2030    #[inline(always)]
2031    pub(crate) fn try_with_no_subscription<T, U>(
2032        &self,
2033        runtime: &Runtime,
2034        f: impl FnOnce(&T) -> U,
2035    ) -> Result<U, SignalError>
2036    where
2037        T: 'static,
2038    {
2039        let value = self.try_with_no_subscription_inner(runtime)?;
2040        let value = value.borrow();
2041        let value = value
2042            .downcast_ref::<T>()
2043            .ok_or_else(|| SignalError::Type(std::any::type_name::<T>()))
2044            .expect("to downcast signal type");
2045        Ok(f(value))
2046    }
2047
2048    #[track_caller]
2049    #[inline(always)]
2050    pub(crate) fn try_with<T, U>(
2051        &self,
2052        runtime: &Runtime,
2053        f: impl FnOnce(&T) -> U,
2054        diagnostics: AccessDiagnostics,
2055    ) -> Result<U, SignalError>
2056    where
2057        T: 'static,
2058    {
2059        self.subscribe(runtime, diagnostics);
2060
2061        self.try_with_no_subscription(runtime, f)
2062    }
2063
2064    #[inline(always)]
2065    #[track_caller]
2066    fn update_value<T, U>(
2067        &self,
2068
2069        f: impl FnOnce(&mut T) -> U,
2070        #[cfg(debug_assertions)] defined_at: Option<
2071            &'static std::panic::Location<'static>,
2072        >,
2073    ) -> Option<U>
2074    where
2075        T: 'static,
2076    {
2077        #[cfg(debug_assertions)]
2078        let location = std::panic::Location::caller();
2079
2080        with_runtime(|runtime| {
2081            if let Some(value) = runtime.get_value(*self) {
2082                let mut value = value.borrow_mut();
2083                if let Some(value) = value.downcast_mut::<T>() {
2084                    Some(f(value))
2085                } else {
2086                    debug_warn!(
2087                        "[Signal::update] failed when downcasting to \
2088                         Signal<{}>",
2089                        std::any::type_name::<T>()
2090                    );
2091                    None
2092                }
2093            } else {
2094                #[cfg(debug_assertions)]
2095                {
2096                    if let Some(defined_at) = defined_at {
2097                        debug_warn!(
2098                            "[Signal::update] At {:?}, you’re trying to \
2099                             update a Signal<{}> (defined at {defined_at}) \
2100                             that has already been disposed of. This is \
2101                             probably a logic error in a component that \
2102                             creates and disposes of scopes. If it does not \
2103                             cause any issues, it is safe to ignore this \
2104                             warning, which occurs only in debug mode.",
2105                            location,
2106                            std::any::type_name::<T>()
2107                        );
2108                    }
2109                }
2110                None
2111            }
2112        })
2113        .unwrap_or_default()
2114    }
2115
2116    #[inline(always)]
2117    #[track_caller]
2118    pub(crate) fn update<T, U>(
2119        &self,
2120        f: impl FnOnce(&mut T) -> U,
2121        #[cfg(debug_assertions)] defined_at: Option<
2122            &'static std::panic::Location<'static>,
2123        >,
2124    ) -> Option<U>
2125    where
2126        T: 'static,
2127    {
2128        #[cfg(debug_assertions)]
2129        let location = std::panic::Location::caller();
2130
2131        with_runtime(|runtime| {
2132            let updated = if let Some(value) = runtime.get_value(*self) {
2133                let mut value = value.borrow_mut();
2134                if let Some(value) = value.downcast_mut::<T>() {
2135                    Some(f(value))
2136                } else {
2137                    debug_warn!(
2138                        "[Signal::update] failed when downcasting to \
2139                         Signal<{}>",
2140                        std::any::type_name::<T>()
2141                    );
2142                    None
2143                }
2144            } else {
2145                #[cfg(debug_assertions)]
2146                {
2147                    if let Some(defined_at) = defined_at {
2148                        debug_warn!(
2149                            "[Signal::update] At {:?}, you’re trying to \
2150                             update a Signal<{}> (defined at {defined_at}) \
2151                             that has already been disposed of. This is \
2152                             probably a logic error in a component that \
2153                             creates and disposes of scopes. If it does not \
2154                             cause any issues, it is safe to ignore this \
2155                             warning, which occurs only in debug mode.",
2156                            location,
2157                            std::any::type_name::<T>()
2158                        );
2159                    }
2160                }
2161                None
2162            };
2163
2164            // notify subscribers
2165            if updated.is_some() {
2166                // mark descendants dirty
2167                runtime.mark_dirty(*self);
2168
2169                runtime.run_effects();
2170            }
2171
2172            updated
2173        })
2174        .unwrap_or_default()
2175    }
2176
2177    #[inline(always)]
2178    pub(crate) fn update_with_no_effect<T, U>(
2179        &self,
2180
2181        f: impl FnOnce(&mut T) -> U,
2182        #[cfg(debug_assertions)] defined_at: Option<
2183            &'static std::panic::Location<'static>,
2184        >,
2185    ) -> Option<U>
2186    where
2187        T: 'static,
2188    {
2189        // update the value
2190        self.update_value(
2191            f,
2192            #[cfg(debug_assertions)]
2193            defined_at,
2194        )
2195    }
2196}
2197
2198#[cold]
2199#[inline(never)]
2200#[track_caller]
2201pub(crate) fn format_signal_warning(
2202    msg: &str,
2203    #[cfg(any(debug_assertions, feature = "ssr"))]
2204    defined_at: &'static std::panic::Location<'static>,
2205) -> String {
2206    let location = std::panic::Location::caller();
2207
2208    let defined_at_msg = {
2209        #[cfg(any(debug_assertions, feature = "ssr"))]
2210        {
2211            format!("signal created here: {defined_at}\n")
2212        }
2213
2214        #[cfg(not(any(debug_assertions, feature = "ssr")))]
2215        {
2216            String::default()
2217        }
2218    };
2219
2220    format!("{msg}\n{defined_at_msg}warning happened here: {location}",)
2221}
2222
2223#[cold]
2224#[inline(never)]
2225#[track_caller]
2226pub(crate) fn panic_getting_dead_signal(
2227    #[cfg(any(debug_assertions, feature = "ssr"))]
2228    defined_at: &'static std::panic::Location<'static>,
2229) -> ! {
2230    panic!(
2231        "{}",
2232        format_signal_warning(
2233            "Attempted to get a signal after it was disposed.",
2234            #[cfg(any(debug_assertions, feature = "ssr"))]
2235            defined_at,
2236        )
2237    )
2238}
2239
2240#[cold]
2241#[inline(never)]
2242#[track_caller]
2243pub(crate) fn warn_updating_dead_signal(
2244    #[cfg(any(debug_assertions, feature = "ssr"))]
2245    defined_at: &'static std::panic::Location<'static>,
2246) {
2247    console_warn(&format_signal_warning(
2248        "Attempted to update a signal after it was disposed.",
2249        #[cfg(any(debug_assertions, feature = "ssr"))]
2250        defined_at,
2251    ));
2252}