dinamika_core/signal/computed.rs
1//! A read-only derived (computed) signal ([`Computed`]).
2
3use std::rc::Rc;
4
5/// A derived (computed) signal — read-only.
6///
7/// The value is recomputed on each [`Computed::get`] from the captured signals,
8/// so it is always up to date:
9///
10/// ```
11/// use dinamika_core::*;
12///
13/// let a = Signal::new(2.0_f32);
14/// let b = Signal::new(3.0_f32);
15/// let sum = {
16/// let (a, b) = (a.clone(), b.clone());
17/// Computed::new(move || a.get() + b.get())
18/// };
19/// assert_eq!(sum.get(), 5.0);
20/// a.set(10.0);
21/// assert_eq!(sum.get(), 13.0);
22/// ```
23pub struct Computed<T> {
24 f: Rc<dyn Fn() -> T>,
25}
26
27impl<T> Clone for Computed<T> {
28 fn clone(&self) -> Self {
29 Computed { f: Rc::clone(&self.f) }
30 }
31}
32
33impl<T> Computed<T> {
34 /// Creates a computed signal from a closure.
35 pub fn new(f: impl Fn() -> T + 'static) -> Self {
36 Computed { f: Rc::new(f) }
37 }
38
39 /// Recomputes and returns the current value.
40 pub fn get(&self) -> T {
41 (self.f)()
42 }
43}
44
45#[cfg(test)]
46mod tests {
47 use super::*;
48 use crate::signal::Signal;
49
50 #[test]
51 fn computed_tracks_sources() {
52 let x = Signal::new(4.0_f32);
53 let doubled = {
54 let x = x.clone();
55 Computed::new(move || x.get() * 2.0)
56 };
57 assert_eq!(doubled.get(), 8.0);
58 x.set(5.0);
59 assert_eq!(doubled.get(), 10.0);
60 }
61}