Trait Differ

Source
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§

Source

type Ok

Type returned on success.

Source

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.

Source

type StructDiffer: StructDiffer<Ok = Self::Ok, Err = Self::Err>

The type we turn into when diffing a struct.

Source

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.

Source

type TupleDiffer: TupleDiffer<Ok = Self::Ok, Err = Self::Err>

The type we turn into when diffing a tuple or tuple struct.

Source

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.

Source

type SeqDiffer: SeqDiffer<Ok = Self::Ok, Err = Self::Err>

The type we turn into when diffing an abstract sequence.

Source

type MapDiffer: MapDiffer<Ok = Self::Ok, Err = Self::Err>

The type we turn into when diffing an abstract map.

Source

type SetDiffer: SetDiffer<Ok = Self::Ok, Err = Self::Err>

The type we turn into when diffing an abstract set.

Required Methods§

Source

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.

Source

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.

Source

fn diff_newtype<T>( self, ty: &'static str, a: &T, b: &T, ) -> Result<Self::Ok, Self::Err>
where T: Diff + ?Sized,

Encounter a newtype. a and b are the contents of the sole fields of the left-hand and right-hand value, respectively.

Source

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()
    }
}
Source

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),
        }
    }
}
Source

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()
    }
}
Source

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),
        }
    }
}
Source

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()
    }
}
Source

fn begin_map(self) -> Self::MapDiffer

Begin traversing a map.

Source

fn begin_set(self) -> Self::SetDiffer

Begin traversing a set.

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 ()

Source§

fn begin_struct(self, _: &'static str) -> Self::StructDiffer

Begin traversing a struct.

Source§

type Ok = ()

Source§

type Err = Void

Source§

type StructDiffer = ()

Source§

type StructVariantDiffer = ()

Source§

type TupleDiffer = ()

Source§

type TupleVariantDiffer = ()

Source§

type SeqDiffer = ()

Source§

type MapDiffer = ()

Source§

type SetDiffer = ()

Source§

fn difference(self, _: &dyn Debug, _: &dyn Debug) -> Result<Self::Ok, Self::Err>

Source§

fn same(self, _: &dyn Debug, _: &dyn Debug) -> Result<Self::Ok, Self::Err>

Source§

fn diff_newtype<T>( self, _: &'static str, _: &T, _: &T, ) -> Result<Self::Ok, Self::Err>
where T: Diff + ?Sized,

Source§

fn begin_struct_variant( self, _: &'static str, _: &'static str, ) -> Self::StructVariantDiffer

Source§

fn begin_tuple(self, _: &'static str) -> Self::TupleDiffer

Source§

fn begin_tuple_variant( self, _: &'static str, _: &'static str, ) -> Self::TupleVariantDiffer

Source§

fn begin_seq(self) -> Self::SeqDiffer

Source§

fn begin_map(self) -> Self::MapDiffer

Source§

fn begin_set(self) -> Self::SetDiffer

Implementors§