pub struct Signal<T> { /* private fields */ }Expand description
Mutable UI-local reactive cell with retained dependency observation.
Clones alias the same RefCell, source revision, historical invalidation
callback, and dynamic retained consumers. A successful read during Build,
Layout, or Paint records the innermost exact consumer; a read outside such a
callback is passive. After a retained callback succeeds, its newly observed
sources replace its previous sources as one update. This makes conditional
reads follow the branch that actually committed; a panic keeps the previous
observations. Unmounting a consumer removes its observations while leaving
externally owned signals readable and mutable.
Mutations commit the value and revision, snapshot live retained consumers,
release all value and subscriber borrows, notify those consumers, and then
invoke the historical callback. Do not rely on ordering among retained
consumers. Notification is synchronous and reentrant; if a retained consumer
panics, later consumers and the historical callback are not invoked for that
mutation. The Rc-based handle is neither Send nor Sync and must remain
on its owning UI thread.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(1)), Rc::new(|| {}));
let alias = signal.clone();
alias.set(2);
assert_eq!(signal.read(), 2);
assert_eq!(signal.revision(), 1);Implementations§
Source§impl<T> Signal<T>
Provides the operations defined for Signal<T>.
impl<T> Signal<T>
Provides the operations defined for Signal<T>.
Sourcepub fn new(value: Rc<RefCell<T>>, invalidate: Rc<dyn Fn()>) -> Self
pub fn new(value: Rc<RefCell<T>>, invalidate: Rc<dyn Fn()>) -> Self
Creates a signal around caller-owned storage and a historical callback.
The initial revision is zero. Multiple calls using the same value do
not share revisions unless the resulting Signal itself is cloned.
The callback is the historical, owner-provided invalidator: it may capture
UI scheduling state and runs synchronously after exact retained consumers
on every successful mutation. It remains installed even when no retained
consumer has observed the signal. Reads establish dynamic dependencies
independently of this callback.
§Examples
use std::{cell::{Cell, RefCell}, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let calls = Rc::new(Cell::new(0));
let observed = calls.clone();
let signal = Signal::new(Rc::new(RefCell::new("a")), Rc::new(move || observed.set(observed.get() + 1)));
signal.set("b");
assert_eq!(calls.get(), 1);Sourcepub fn set(&self, value: T)
pub fn set(&self, value: T)
Replaces the value, advances revision, then notifies all consumers.
Equality is not checked, so setting an equal value still invalidates.
Revisions advance from zero to one and wrap from u64::MAX back to one;
compare them for inequality, not ordering.
§Panics
Panics on a conflicting RefCell borrow or when a notification callback
panics. In the callback case, the new value and revision remain committed.
Retained consumers run before the historical callback; a retained panic
stops the remaining notification sequence. All signal borrows are released
before callbacks run, so a callback may read or mutate the signal again.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(3)), Rc::new(|| {}));
signal.set(3);
assert_eq!(signal.read(), 3);
assert_eq!(signal.revision(), 1);Sourcepub fn update(&self, f: impl FnOnce(&mut T))
pub fn update(&self, f: impl FnOnce(&mut T))
Mutates the value in place, advances revision, then notifies consumers.
The closure is called exactly once under a mutable RefCell borrow.
This method does not detect whether the closure actually changed T.
§Panics
Panics on conflicting borrowing. If f panics, its partial mutation is
retained but revision and notification are skipped. A notification panic
propagates after mutation and revision commit, with the value borrow gone.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(vec![1])), Rc::new(|| {}));
signal.update(|items| items.push(2));
assert_eq!(signal.read(), vec![1, 2]);Sourcepub fn revision(&self) -> u64
pub fn revision(&self) -> u64
Returns and observes the shared mutation revision without borrowing T.
Zero means no successful mutating method has completed. The revision is
nonzero thereafter but can repeat after u64 wraparound. In a retained
observation scope this registers the same source as Self::read.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(0)), Rc::new(|| {}));
assert_eq!(signal.revision(), 0);
signal.set(1);
assert_eq!(signal.revision(), 1);Source§impl<T: PartialEq> Signal<T>
Provides the operations defined for Signal<T>.
impl<T: PartialEq> Signal<T>
Provides the operations defined for Signal<T>.
Sourcepub fn set_if_changed(&self, value: T) -> bool
pub fn set_if_changed(&self, value: T) -> bool
Replaces a small comparable value only when it actually changed.
This avoids needless invalidations for scalar/domain revision signals; it is not intended as a substitute for retained models or for comparing complete trees.
A false result performs no invalidation and does not advance revision.
A true result commits the value before synchronously invalidating.
§Panics
Panics on a conflicting RefCell borrow, a panicking equality operation,
or a panicking invalidation callback.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(4)), Rc::new(|| {}));
assert!(!signal.set_if_changed(4));
assert!(signal.set_if_changed(5));
assert_eq!((signal.read(), signal.revision()), (5, 1));Source§impl<T: Clone> Signal<T>
Provides the operations defined for Signal<T>.
impl<T: Clone> Signal<T>
Provides the operations defined for Signal<T>.
Sourcepub fn read(&self) -> T
pub fn read(&self) -> T
Clones, observes, and returns the current value.
§Panics
A successful read is attributed only to the innermost retained build, layout, or paint callback. Outside those callbacks it schedules no future frame. Panics when the value is already mutably borrowed through another alias.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(String::from("hello"))), Rc::new(|| {}));
let copy = signal.read();
assert_eq!(copy, "hello");Sourcepub fn map<U: 'static>(&self, f: impl Fn(T) -> U + 'static) -> Memo<U>where
T: 'static,
pub fn map<U: 'static>(&self, f: impl Fn(T) -> U + 'static) -> Memo<U>where
T: 'static,
Creates a lazy derived value whose revision mirrors this signal.
No value is cached: every Memo::read clones the current T and calls
f. The mapping itself installs no additional historical callback.
Reading the memo transitively observes this signal in the active retained
consumer scope.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(2)), Rc::new(|| {}));
let doubled = signal.map(|value| value * 2);
assert_eq!(doubled.read(), 4);
signal.set(3);
assert_eq!((doubled.read(), doubled.revision()), (6, 1));Source§impl<T> Signal<T>where
T: Clone + 'static,
impl<T> Signal<T>where
T: Clone + 'static,
Sourcepub fn to_text_with<F>(&self, format: F) -> Memo<String>
pub fn to_text_with<F>(&self, format: F) -> Memo<String>
Creates a lazy string memo using a custom owned-value formatter.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(3)), Rc::new(|| {}));
assert_eq!(signal.to_text_with(|value| format!("#{value}")).read(), "#3");Source§impl<T: Default> Signal<T>
Provides the operations defined for Signal<T>.
impl<T: Default> Signal<T>
Provides the operations defined for Signal<T>.
Sourcepub fn take(&self) -> T
pub fn take(&self) -> T
Replaces the current value with Default and returns the old value.
Revision and invalidation always occur, even when the old value already equaled its default.
§Panics
Panics on conflicting borrowing or a panicking invalidation callback.
§Examples
use std::{cell::RefCell, rc::Rc};
use ailloli_ui_runtime::component::Signal;
let signal = Signal::new(Rc::new(RefCell::new(String::from("take"))), Rc::new(|| {}));
assert_eq!(signal.take(), "take");
assert_eq!(signal.take(), "");
assert_eq!(signal.revision(), 2);Trait Implementations§
Auto Trait Implementations§
impl<T> Freeze for Signal<T>
impl<T> !RefUnwindSafe for Signal<T>
impl<T> !Send for Signal<T>
impl<T> !Sync for Signal<T>
impl<T> Unpin for Signal<T>
impl<T> !UnwindSafe for Signal<T>
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<A> IntoClickAction<A> for Awhere
A: Clone + 'static,
impl<A> IntoClickAction<A> for Awhere
A: Clone + 'static,
Source§fn into_click_action(self) -> ClickAction<A>
fn into_click_action(self) -> ClickAction<A>
Implements the into_click_action helper used by this module.