spidermeme 0.1.0

Traits to test for type equality and type inequality
Documentation
WARNING: This crate currently depends on nightly rust unstable features.

Provided traits

spidermeme::SameTypeAs<T>

An automatically implemented marker trait to check if two types are equal.

spidermeme::NotSameTypeAs<T>

An automatically implemented marker trait to check if two types aren't equal.

Examples

use spidermeme::{SameTypeAs, NotSameTypeAs};

struct MyPair<T1, T2>(T1, T2);

trait ProcessPair {
    fn process(&self);
}

impl<T1, T2> ProcessPair for MyPair<T1, T2> {
    fn process(&self) {
        println!("Pair of two different types.");
    }
}

impl<T1, T2> MyPair<T1, T2>
where
    T1: SameTypeAs<T2>,
{
    fn process(&self) {
        println!("Pair of same type.");
    }
}

struct UniquePair<T1, T2>
where
    T1: NotSameTypeAs<T2>,
{
    a: T1,
    b: T2,
}

impl<T1, T2> UniquePair<T1, T2>
where
    T1: NotSameTypeAs<T2>,
{
    pub fn new(a: T1, b: T2) -> Self {
        Self { a, b }
    }
}

fn main() {
    // Prints "Pair of same type."
    MyPair(1_i32, 2_i32).process();

    // Prints "Pair of two different types."
    MyPair(1_i32, 2_i16).process();

    // Valid.
    let x = UniquePair::<i32, f64>::new(1, 2.0);

    // The following fails to compile:
    // let y = UniquePair::<i32, i32>::new(1, 2);
}

How type equality works

Type equality is pretty straightforward. The [SameTypeAs] trait has a blanket implementation using the same generic parameter. The basic principle looks like this when simplified:

pub trait Same<T> {}
impl<T> Same<T> for T {}

This was inspired by numerous comments floating around on the web.

How type inequality works

Type inequality uses negative_impls and auto_traits. A naive implementation would be like the following:

#![feature(negative_impls)]
#![feature(auto_traits)]
pub auto trait DifferentNaive {}
impl<T> !DifferentNaive for (T, T) {}

However, this will give false negatives, as the auto trait will not be implemented for types that contain (T, T). For example, the naive implementation will fail in the following example because ((i32, i32), (f64, f64)) contains (i32, i32) and (f64, f64), both of which implement the DifferentNaive trait:

use static_assertions::assert_impl_all;
assert_impl_all!(((i32, i32), (f64, f64)): DifferentNaive);

This crate works around this by using a private named tuple instead of the primitive tuple, so that it is guaranteed that downstream crates will not test types that contain this named tuple.

Known problems / quirks

  1. Using both [SameTypeAs] and [NotSameTypeAs] to implement two impls for the same type will give: error[E0119]: conflicting implementations, probably due to the current limitations of Rust(?).

  2. References to the same type, but with different lifetimes, are treated the same. This could be thought of as a "feature" if you squint hard enough.

  3. For type equality in serious projects, you should probably try some other crates by people who probably know better type theory and rust's type system.

Unstable features

#![feature(negative_impls)]
#![feature(auto_traits)]
#![feature(extended_key_value_attributes)]