Skip to main content

ferroui_system/component_model/
backing_field.rs

1//! The backing field of a notifying property: the `ref T backingField` of the setters of .NET
2//! (`SetAndRaise`, `RaiseAndSetIfChanged`).
3
4use std::cell::{Cell, RefCell};
5
6/// A backing field that a setter reads and assigns through a shared reference: a `Cell` for a
7/// value that is `Copy`, a `RefCell` for the others (`ref T backingField`).
8///
9/// The setters of the reactive libraries take `&impl BackingField<T>`, so that one setter serves
10/// both kinds of field.
11///
12/// ```
13/// use ferroui_system::component_model::BackingField;
14/// use std::cell::{Cell, RefCell};
15///
16/// fn set<T: PartialEq>(field: &impl BackingField<T>, value: T) -> bool {
17///     if field.equals(&value, &|a, b| a == b) {
18///         return false;
19///     }
20///     field.store(value);
21///     true
22/// }
23///
24/// let age = Cell::new(3);
25/// assert!(set(&age, 4));
26/// assert!(!set(&age, 4));
27/// let name = RefCell::new(String::from("a"));
28/// assert!(set(&name, String::from("b")));
29/// assert_eq!(*name.borrow(), "b");
30/// ```
31pub trait BackingField<T> {
32    /// Whether the value of the field equals `value` by `comparer`.
33    fn equals(&self, value: &T, comparer: &dyn Fn(&T, &T) -> bool) -> bool;
34    /// Assigns `value` to the field.
35    fn store(&self, value: T);
36}
37
38impl<T: Copy> BackingField<T> for Cell<T> {
39    fn equals(&self, value: &T, comparer: &dyn Fn(&T, &T) -> bool) -> bool {
40        comparer(&self.get(), value)
41    }
42
43    fn store(&self, value: T) {
44        self.set(value);
45    }
46}
47
48impl<T> BackingField<T> for RefCell<T> {
49    fn equals(&self, value: &T, comparer: &dyn Fn(&T, &T) -> bool) -> bool {
50        comparer(&self.borrow(), value)
51    }
52
53    fn store(&self, value: T) {
54        // The old value is dropped after the borrow ends, so that its destructor may read the
55        // field.
56        let old = std::mem::replace(&mut *self.borrow_mut(), value);
57        drop(old);
58    }
59}