Trait syllogism::Specialize[][src]

pub trait Specialize<T>: Sized {
    fn specialize(self) -> Distinction<T, Self>;
}
Expand description

A trait allowing to distinguish between the special case and the generic case at run time.

See the crate level documentation for more info.

Below we describe various methods for implementing the Specialize trait. The easiest way however is probably to use the syllogism-macro crate.

Implementing Specialize using the IsNot trait

One shortcut you can take if the data type has no type parameters is to use the IsNot trait, assuming you already need to implement this.

use syllogism::{IsNot, Specialize, Distinction};

struct T1 { /* ... */ }
struct T2 { /* ... */ }
struct T3 { /* ... */ }

impl IsNot<T2> for T1 {}
impl IsNot<T3> for T1 {}
impl IsNot<T1> for T2 {}
impl IsNot<T3> for T2 {}
impl IsNot<T1> for T3 {}
impl IsNot<T2> for T3 {}

impl<T> Specialize<T> for T1
where T: IsNot<T1>
{
    fn specialize(self) -> Distinction<T, Self> {
        Distinction::Generic(self)
    }
}

impl Specialize<T1> for T1 {
    fn specialize(self) -> Distinction<T1, Self> {
        Distinction::Special(self)
    }
}

// And similar for `T2` and `T3`.

Implementing Specialize for types with a type parameter using the Specialize trait

When a type has a type parameter, in some circumstances, you can not use the IsNot trait to implement the Specialize trait: the following code will fail to compile

use syllogism::{IsNot, Specialize, Distinction};

struct T1<T> {
    t: T
    // ...
}

impl<T, U> IsNot<T1<T>> for T1<U> where T: IsNot<U> {}

impl<T, U> Specialize<T1<T>> for T1<U> where T: IsNot<U>{
    fn specialize(self) -> Distinction<T, Self> {
        Distinction::Generic(self)
    }
}
impl<T> Specialize<T1<T>> for T1<T> {
    fn specialize(self) -> Distinction<T1<T>, Self> {
        Distinction::Special(self)
    }
}

The reason for this compile failure is that a type Ts may implement IsNot<Ts> and in that case, T1<Ts> implements IsNot<T1<Ts>> and the impl blocks overlap.

This can be solved by using the Specialize trait again:

use syllogism::{IsNot, Specialize, Distinction};

struct T1<T> {
    t: T,
    other_field: u8
}

impl<T, U> IsNot<T1<T>> for T1<U>
where T: IsNot<U> {
}

impl<T, U> Specialize<T1<T>> for T1<U>
where U: Specialize<T>
{
    fn specialize(self) -> Distinction<T1<T>, Self> {
        let T1{
            t,
            other_field
        } = self; // deconstructing `let`

        match t.specialize() {
            Distinction::Special(special) => {
                Distinction::Special(T1{t: special, other_field})
            },
            Distinction::Generic(generic) => {
                Distinction::Generic(T1{t: generic, other_field})
            }
        }
    }
}

Implementing Specialize using macros

Implementing Specialize correctly for each pair of data types at hand can be error prone. E.g. if there are three data types at play, T1, T2, T3. If you want to allow specialization for any of these data types, giving the other two the “default” treatment, then you need nine implementations of the Specialize trait:

use syllogism::{Specialize, Distinction};

struct T1 { /* ... */ }
struct T2 { /* ... */ }
struct T3 { /* ... */ }

impl Specialize<T1> for T1 {
    fn specialize(self) -> Distinction<T1, Self> {
        Distinction::Special(self)
    }
}

impl Specialize<T2> for T1 {
    fn specialize(self) -> Distinction<T2, Self> {
        Distinction::Generic(self)
    }
}

impl Specialize<T3> for T1 {
    // similar
}

impl Specialize<T1> for T2 {
    // similar
}
impl Specialize<T2> for T2 {
    // similar
}
impl Specialize<T3> for T2 {
    // similar
}

impl Specialize<T1> for T3 {
    // similar
}
impl Specialize<T2> for T3 {
    // similar
}
impl Specialize<T3> for T3 {
    // similar
}

The syllogism-macro crate provides procedural macros to generate these impls for you, and also offers some techniques that allow extending the set of data types across crate boundaries. We refer to the documentation of that crate for more information.

Required methods

Implementors