pub trait Diff {
// Required method
fn diff<E: EventQueue>(
&self,
baseline: &Self,
path: PathBuilder,
event_queue: &mut E,
);
}Expand description
Fine-grained parameter diffing.
This trait allows a type to perform diffing on itself, generating events that another instance can use to patch itself.
For more information, see the module docs.
§Examples
For most use cases, Diff is fairly straightforward.
use firewheel_core::diff::{Diff, PathBuilder};
#[derive(Diff, Clone)]
struct MyParams {
a: f32,
b: f32,
}
let mut params = MyParams {
a: 1.0,
b: 1.0,
};
// This "baseline" instance allows us to keep track
// of what's changed over time.
let baseline = params.clone();
// A single mutation to a "leaf" type like `f32` will
// produce a single event.
params.a = 0.5;
// `Vec<NodeEventType>` implements `EventQueue`, meaning we
// don't necessarily need to keep track of `NodeID`s for event generation.
let mut event_queue = Vec::new();
// Top-level calls to diff should always provide a default path builder.
params.diff(&baseline, PathBuilder::default(), &mut event_queue);
assert_eq!(event_queue.len(), 1);When using Firewheel in a standalone context, the Memo type can
simplify this process.
use firewheel_core::diff::Memo;
let mut params_memo = Memo::new(MyParams {
a: 1.0,
b: 1.0,
});
// `Memo` implements `DerefMut` on the wrapped type, allowing you
// to use it almost transparently.
params_memo.a = 0.5;
let mut event_queue = Vec::new();
// This generates patches and brings the internally managed
// baseline in sync.
params_memo.update_memo(&mut event_queue);§Manual implementation
Aggregate types like parameters should prefer the derive macro, but manual implementations can occasionally be handy. You should strive to match the derived data model for maximum compatibility.
use firewheel_core::diff::{Diff, PathBuilder, EventQueue};
impl Diff for MyParams {
fn diff<E: EventQueue>(&self, baseline: &Self, path: PathBuilder, event_queue: &mut E) {
// The diffing data model requires a unique path to each field.
// Because this type can be arbitrarily nested, you should always
// extend the provided path builder using `PathBuilder::with`.
//
// Because this is the first field, we'll extend the path with 0.
self.a.diff(&baseline.a, path.with(0), event_queue);
self.b.diff(&baseline.b, path.with(1), event_queue);
}
}You can easily override a type’s Diff implementation by simply
doing comparisons by hand.
use firewheel_core::event::ParamData;
impl Diff for MyParams {
fn diff<E: EventQueue>(&self, baseline: &Self, path: PathBuilder, event_queue: &mut E) {
// The above is essentially equivalent to:
if self.a != baseline.a {
event_queue.push_param(ParamData::F32(self.a), path.with(0));
}
if self.b != baseline.b {
event_queue.push_param(ParamData::F32(self.b), path.with(1));
}
}
}If your type has invariants between fields that must not be violated, you
can consider the whole type a “leaf,” similar to how Diff is implemented
on primitives. Depending on the type’s data, you may require an allocation.
impl Diff for MyParams {
fn diff<E: EventQueue>(&self, baseline: &Self, path: PathBuilder, event_queue: &mut E) {
if self != baseline {
// Note that if we consider the whole type to be a leaf, there
// is no need to extend the path.
event_queue.push_param(ParamData::any(self.clone()), path);
}
}
}Required Methods§
Sourcefn diff<E: EventQueue>(
&self,
baseline: &Self,
path: PathBuilder,
event_queue: &mut E,
)
fn diff<E: EventQueue>( &self, baseline: &Self, path: PathBuilder, event_queue: &mut E, )
Compare self to baseline and generate events to resolve any differences.
Dyn Compatibility§
This trait is not dyn compatible.
In older versions of Rust, dyn compatibility was called "object safety", so this trait is not object safe.