pub enum Patch<T: Clone> {
Box(Box<dyn FnMut(&mut T, &T) + Send + 'static>),
Arc(Arc<dyn Fn(&mut T, &T) + Send + Sync + 'static>),
Ptr(fn(_: &mut T, _: &T)),
}
Expand description
Functions to be applied to your data T
.
Patch
is just a function that will be applied to your data T
.
The Writer
expects T
modifications in the form of Patch
’s.
The enumrated options are various forms of functions.
The 2 inputs you are given are:
- The
Writer
’s local mutable data,T
(the thing you’re modifying) - The
Reader
’s latest head commit
let (_, mut w) = someday::new::<String>("".into());
// Use a pre-defined function pointer.
fn fn_ptr(w: &mut String, r: &String) {
w.push_str("hello");
}
w.add(Patch::Ptr(fn_ptr));
// This non-capturing closure can also
// be coerced into a function pointer.
w.add(Patch::Ptr(|w, _| {
w.push_str("hello");
}));
// This capturing closure gets turned into
// a cheaply clone-able dynamic function.
let string = String::from("hello");
w.add(Patch::arc(move |w: &mut String ,_| {
let captured = &string;
w.push_str(&captured);
}));
§⚠️ Non-deterministic Patch
The Patch
’s you use with Writer::add
must be deterministic.
The Writer
may apply your Patch
twice, so any state that gets
modified or functions used in the Patch
must result in the
same values as the first time the Patch
was called.
Here is a non-deterministic example:
static STATE: Mutex<usize> = Mutex::new(1);
let (_, mut w) = someday::new::<usize>(0);
w.add(Patch::boxed(move |w, _| {
let mut state = STATE.lock().unwrap();
*state *= 10; // 1*10 the first time, 10*10 the second time...
*w = *state;
}));
w.commit();
w.push();
// ⚠️⚠️⚠️ !!!
// The `Writer` reclaimed the old `Reader` data
// and applied our `Patch` again, except, the `Patch`
// was non-deterministic, so now the `Writer`
// and `Reader` have non-matching data...
assert_eq!(*w.data(), 100);
assert_eq!(w.reader().head().data, 10);
§The 2nd apply
Note that if/when the Writer
applies your Patch
for the 2nd time
inside Writer::push
, the Reader
side of the data has just been updated.
This means your Patch
’s 2nd input &T
will be referencing the just pushed data.
let (_, mut writer) = someday::new::<usize>(0);
writer.add(Patch::Ptr(|w, r| {
// `w` on the first apply of this Patch
// is our local Writer data. `r` is the
// current `Reader` data (whether out-of-date or not).
//
// The 2nd time this applies, `w` will be
// the old `Reader` data we are attempting
// to reclaim and "reproduce" with this Patch,
// while `r` will be the data the `Writer` just pushed.
}));
Variants§
Box(Box<dyn FnMut(&mut T, &T) + Send + 'static>)
Dynamically dispatched, potentially capturing, boxed function.
let string = String::new();
let mut boxed: Box<dyn FnMut()> = Box::new(move || {
// The outside string was captured.
println!("{string}");
});
// This cannot be cloned.
boxed();
Arc(Arc<dyn Fn(&mut T, &T) + Send + Sync + 'static>)
Dynamically dispatched, potentially capturing, cheaply Clone
-able function.
let string = String::new();
let arc: Arc<dyn Fn()> = Arc::new(move || {
// The outside string was captured.
println!("{string}");
});
// We can clone this as much as we want though.
let arc2 = Arc::clone(&arc);
let arc3 = Arc::clone(&arc);
arc();
arc2();
arc3();
Ptr(fn(_: &mut T, _: &T))
Non-capturing, static function pointer.
let ptr: fn() = || {
// Nothing was captured.
//
// This closure can be coerced into
// a function pointer, same as `fn()`.
let string = String::new();
println!("{string}");
};
// Can copy it infinitely, it's just a pointer.
let ptr2 = ptr;
let ptr3 = ptr;
ptr();
ptr2();
ptr3();
Implementations§
source§impl<T: Clone> Patch<T>
impl<T: Clone> Patch<T>
sourcepub fn boxed<P>(patch: P) -> Self
pub fn boxed<P>(patch: P) -> Self
Short-hand for Self::Box(Box::new(patch))
.
let string = String::new();
let boxed_patch = Patch::<String>::boxed(move |_, _| {
let captured_variable = &string;
});
assert!(boxed_patch.is_box());