pub trait Differ {
type Ok;
type Err;
type StructDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>;
type StructVariantDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>;
type TupleDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>;
type TupleVariantDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>;
type SeqDiffer: SeqDiffer<Ok = Self::Ok, Err = Self::Err>;
type MapDiffer: MapDiffer<Ok = Self::Ok, Err = Self::Err>;
type SetDiffer: SetDiffer<Ok = Self::Ok, Err = Self::Err>;
// Required methods
fn difference(
self,
a: &dyn Debug,
b: &dyn Debug,
) -> Result<Self::Ok, Self::Err>;
fn same(self, a: &dyn Debug, b: &dyn Debug) -> Result<Self::Ok, Self::Err>;
fn diff_newtype<T>(
self,
ty: &'static str,
a: &T,
b: &T,
) -> Result<Self::Ok, Self::Err>
where T: Diff + ?Sized;
fn begin_struct(self, ty: &'static str) -> Self::StructDiffer;
fn begin_struct_variant(
self,
ty: &'static str,
var: &'static str,
) -> Self::StructVariantDiffer;
fn begin_tuple(self, ty: &'static str) -> Self::TupleDiffer;
fn begin_tuple_variant(
self,
ty: &'static str,
var: &'static str,
) -> Self::TupleVariantDiffer;
fn begin_seq(self) -> Self::SeqDiffer;
fn begin_map(self) -> Self::MapDiffer;
fn begin_set(self) -> Self::SetDiffer;
}
Expand description
A type that can do something with information about structural differences.
If you think that sounds vague, you’re right! This trait is very general and covers a lot of use cases, which can make it hard to understand at first glance. Don’t worry! It’s straightforward once you get the hang of it, though it is pretty wordy.
§How to use a Differ
Normally, you’ll only call the methods on Differ
from within an
implementation of Diff::diff
. Also, normally, you won’t write your own
implementation of Diff::diff
in the first place – you’ll
#[derive(Diff)]
. This section will explain how to use Differ
manually to
produce the same results as the derived impls, should you ever need to.
An implementation of Differ
is actually a small family of types. There’s
the type implementing Differ
(which we’ll call “the differ”) for short,
and then there are the associated types. All of the methods on Differ
either produce a result immediately, or convert the differ into one of the
associated types because more information is needed to produce a result.
In the end, every complete interaction with a differ type D
produces the
same type: Result<D::Ok, D::Err>
. This means each implementation can
decide what its output and failure types look like.
The basic methods difference
, same
, and diff_newtype
produce a
result immediately without further work.
The methods starting with begin
require more than one step.
§struct
If a type is a struct with named fields, call begin_struct
to convert
the Differ
into a StructDiffer
. StructDiffer
has methods for
describing struct fields. See the example on begin_struct
for more.
Not all structs have named fields: there are also tuple structs. For a tuple
struct, call begin_tuple
to convert the Differ
into a TupleDiffer
.
TupleDiffer
has methods for describing tuple struct fields. See the
example on begin_tuple
for more.
§enum
Rust enums are more complex than structs, because each variant of an enum can have a different shape: some may have named fields, some may have unnamed fields, and some may be unit variants without fields at all.
Typically you only want to treat two values of an enum type as “same” if
they have the same variant. This means an implementation of diff
for an
enum will usually have a “parallel match” shape like this:
use visit_diff::{Differ, Diff};
#[derive(Debug)]
enum ExampleEnum { Variant1, Variant2 }
impl Diff for ExampleEnum {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where D: Differ
{
match (a, b) {
(ExampleEnum::Variant1, ExampleEnum::Variant1) => {
out.same(a, b)
}
(ExampleEnum::Variant2, ExampleEnum::Variant2) => {
out.same(a, b)
}
_ => out.difference(a, b),
}
}
}
In that example, both variants are unit variants without fields. Let’s consider the other flavors.
For struct variants with named fields, use begin_struct_variant
to
convert the differ into a StructVariantDiffer
. See the example on
begin_struct_variant
for more.
For tuple variants with unnamed fields, use begin_tuple_variant
to
convert the differ into a TupleVariantDiffer
. See the example on
begin_tuple_variant
for more.
§Abstract types
This crate recognizes three kinds of abstract types, which don’t directly
map to any native Rust type, but are really common library types. (They also
happen to be the three kinds of abstract types recognized by
std::fmt::Formatter
.)
Sequences are variable-length ordered collections of things, such as a
slice or a Vec
. Not only can the individual elements be different between
two sequences, but elements can be added or removed, too. For types that
want to be treated like sequences, use begin_seq
to convert the differ
into a SeqDiffer
. See the example on begin_seq
for more.
Sets are variable-length collections of things where each thing appears
only once, such as a HashSet
. Sets may or may not be ordered. They’re
otherwise treated a lot like sequences. For set-like types, use
begin_set
to convert the differ into a SetDiffer
.
Maps are variable-length collections of key-value pairs, like a HashMap
.
Maps may or may not be ordered. For map-like types, use begin_map
to
convert the differ into a MapDiffer
.
Required Associated Types§
Sourcetype Err
type Err
Type returned on failure.
If your differ can’t fail, consider using the void
crate. It
provides an extension method on Result
, unwrap_void
, that never
panics.
Sourcetype StructDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>
type StructDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>
The type we turn into when diffing a struct.
Sourcetype StructVariantDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>
type StructVariantDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>
The type we turn into when diffing a struct variant of an enum.
This is often the same type as StructDiffer
.
Sourcetype TupleDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>
type TupleDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>
The type we turn into when diffing a tuple or tuple struct.
Sourcetype TupleVariantDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>
type TupleVariantDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>
The type we turn into when diffing a tuple variant of an enum.
This is often the same type as TupleDiffer
.
Sourcetype SeqDiffer: SeqDiffer<Ok = Self::Ok, Err = Self::Err>
type SeqDiffer: SeqDiffer<Ok = Self::Ok, Err = Self::Err>
The type we turn into when diffing an abstract sequence.
Required Methods§
Sourcefn difference(self, a: &dyn Debug, b: &dyn Debug) -> Result<Self::Ok, Self::Err>
fn difference(self, a: &dyn Debug, b: &dyn Debug) -> Result<Self::Ok, Self::Err>
Two atomic values have been discovered to be different, such as different numbers or different variants of an enum.
Sourcefn same(self, a: &dyn Debug, b: &dyn Debug) -> Result<Self::Ok, Self::Err>
fn same(self, a: &dyn Debug, b: &dyn Debug) -> Result<Self::Ok, Self::Err>
Two atomic values are the same, such as equal numbers or identical unit variants of an enum.
Sourcefn diff_newtype<T>(
self,
ty: &'static str,
a: &T,
b: &T,
) -> Result<Self::Ok, Self::Err>
fn diff_newtype<T>( self, ty: &'static str, a: &T, b: &T, ) -> Result<Self::Ok, Self::Err>
Encounter a newtype. a
and b
are the contents of the sole fields of
the left-hand and right-hand value, respectively.
Sourcefn begin_struct(self, ty: &'static str) -> Self::StructDiffer
fn begin_struct(self, ty: &'static str) -> Self::StructDiffer
Begin traversing a struct with named fields.
This converts self
into an implementation of StructDiffer
, which
in turn has methods for describing a struct.
Here’s an example of using begin_struct
to manually implement Diff
for a struct with named fields:
use visit_diff::{Diff, Differ};
#[derive(Debug)]
struct ExampleStruct {
name: String,
age: usize,
}
impl Diff for ExampleStruct {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where D: Differ
{
// Bring the struct operations into scope. This could also go at
// the top.
use visit_diff::StructDiffer;
let mut out = out.begin_struct("ExampleStruct");
// Visit each field in turn.
out.diff_field("name", &a.name, &b.name);
out.diff_field("age", &a.age, &b.age);
// Finish the diff and generate the result.
out.end()
}
}
Sourcefn begin_struct_variant(
self,
ty: &'static str,
var: &'static str,
) -> Self::StructVariantDiffer
fn begin_struct_variant( self, ty: &'static str, var: &'static str, ) -> Self::StructVariantDiffer
Begin traversing a struct variant of an enum.
The rest is very similar to dealing with a normal struct, except that we have to use pattern matching to get at the fields.
use visit_diff::{Diff, Differ};
#[derive(Debug)]
enum ExampleEnum {
Unit,
Struct {
name: String,
age: usize,
},
}
impl Diff for ExampleEnum {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where D: Differ
{
match (a, b) {
(ExampleEnum::Unit, ExampleEnum::Unit) => out.same(a, b),
(ExampleEnum::Struct { name: a_name, age: a_age },
ExampleEnum::Struct { name: b_name, age: b_age }) => {
// Bring the struct operations into scope. This could
// also go at the top. Note that struct variants use the
// same trait as normal structs.
use visit_diff::StructDiffer;
let mut out = out.begin_struct_variant(
"ExampleEnum", // type name
"Struct", // variant name
);
// Visit each field in turn.
out.diff_field("name", a_name, b_name);
out.diff_field("age", a_age, b_age);
// Finish the diff and generate the result.
out.end()
}
_ => out.difference(a, b),
}
}
}
Sourcefn begin_tuple(self, ty: &'static str) -> Self::TupleDiffer
fn begin_tuple(self, ty: &'static str) -> Self::TupleDiffer
Begin traversing a tuple struct or raw tuple.
This converts self
into an implementation of TupleDiffer
, which
in turn has methods for describing a tuple struct.
To describe something as a raw tuple (even if it isn’t necessarily), pass an empty string for the type name.
Here’s an example of using begin_tuple
to manually implement Diff
for a struct with unnamed fields:
use visit_diff::{Diff, Differ};
#[derive(Debug)]
struct ExampleStruct(String, usize);
impl Diff for ExampleStruct {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where D: Differ
{
// Bring the tuple operations into scope. This could also go at
// the top.
use visit_diff::TupleDiffer;
let mut out = out.begin_tuple("ExampleStruct");
// Visit each field in turn.
out.diff_field(&a.0, &b.0);
out.diff_field(&a.1, &b.1);
// Finish the diff and generate the result.
out.end()
}
}
Sourcefn begin_tuple_variant(
self,
ty: &'static str,
var: &'static str,
) -> Self::TupleVariantDiffer
fn begin_tuple_variant( self, ty: &'static str, var: &'static str, ) -> Self::TupleVariantDiffer
Begin traversing a tuple variant of an enum.
The rest is very similar to dealing with a normal tuple, except that we have to use pattern matching to get at the fields.
use visit_diff::{Diff, Differ};
#[derive(Debug)]
enum ExampleEnum {
Unit,
Tuple(String, usize),
}
impl Diff for ExampleEnum {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where D: Differ
{
match (a, b) {
(ExampleEnum::Unit, ExampleEnum::Unit) => out.same(a, b),
(ExampleEnum::Tuple(a_name, a_age),
ExampleEnum::Tuple(b_name, b_age)) => {
// Bring the tuple operations into scope. This could
// also go at the top. Note that tuple variants use the
// same trait as normal tuples.
use visit_diff::TupleDiffer;
let mut out = out.begin_tuple_variant(
"ExampleEnum", // type name
"Tuple", // variant name
);
// Visit each field in turn.
out.diff_field(a_name, b_name);
out.diff_field(a_age, b_age);
// Finish the diff and generate the result.
out.end()
}
_ => out.difference(a, b),
}
}
}
Sourcefn begin_seq(self) -> Self::SeqDiffer
fn begin_seq(self) -> Self::SeqDiffer
Begin traversing a sequence.
This is quite general; it’s up to you to decide how exactly your type looks like a sequence.
Here’s a simple implementation for slices – which we wrap in a newtype
here because there’s already an implementation for slices. This uses the
provided diff_elements
method that makes diffing two iterators easy.
use visit_diff::{Diff, Differ};
#[derive(Debug)]
struct Slice<'a, T>(&'a [T]);
impl<'a, T: Diff> Diff for Slice<'a, T> {
fn diff<D>(a: &Self, b: &Self, out: D) -> Result<D::Ok, D::Err>
where D: Differ
{
// Bring the sequence operations into scope. This could also go
// at the top.
use visit_diff::SeqDiffer;
let mut out = out.begin_seq();
out.diff_elements(a.0, b.0);
out.end()
}
}
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.
Implementations on Foreign Types§
Source§impl Differ for ()
impl Differ for ()
Source§fn begin_struct(self, _: &'static str) -> Self::StructDiffer
fn begin_struct(self, _: &'static str) -> Self::StructDiffer
Begin traversing a struct.