Struct imprint::TyEq [] [src]

pub struct TyEq<T: ?Sized, U: ?Sized>(_, _);

Propositional equality between types.

If two types A and B are equal, then it is safe to transmute between A and B as well as any types that contain A or B. The converse is generally not true.

Unsafe: Conjuring equality out of thin air

It is sometimes useful to bypass Rust's type system to create a TyEq<T, U> object where T is not judgmentally equal to U. This can be done by transmutation:

std::mem::transmute::<TyEq<Foo<'a>, Foo<'a>>,
                      TyEq<Foo<'a>, Foo<'b>>>(TyEq::refl())

However, you must be absolutely certain that

  • A and B are truly transmute-compatible (which usually means A and B must differ only in phantom parameters), and
  • changing from A to B or vice versa cannot alter the observable behavior of any valid program.

The second condition is crucial: it is never correct equate two fully concrete types (e.g. between PhantomData<i64> and PhantomData<u64>) even if they are representationally identical, because one can always use traits to dispatch based on the identity of the types, resulting in differences in observable behavior.

Generally, it is only sensible to equate (partially) abstract types (e.g. Foobar<T> and Foobar<U> where T and U are unknown), and even still you have to make sure that this wouldn't cause changes in observable behavior. Most of the time, it only makes sense to equate generic phantom lifetime parameters.

Methods

impl<T: ?Sized> TyEq<T, T>
[src]

Constructor for TyEq (reflexivity).

impl<T: ?Sized, U: ?Sized> TyEq<T, U>
[src]

Substitute instances of T within a type with U (Leibniz's law, a.k.a. indiscernibility of identicals).

The apply function allows you to freely convert between any two types as long as they differ only in T and U. For example, you can turn Vec<(T, T)> into Vec<(T, U)>, Vec<(U, T)>, or Vec<(U, U)>.

The type signature in the auto-generated documentation is unclear. It should've been more like:

fn apply(TyEq<T, U>, FT) -> FU
  where T: TyFn<F, Output=FT>, U: TyFn<F, Output=FU>

In Haskell, it'd be simply TyEq t u -> f t -> f u.

Example

use imprint::{TyEq, TyFn};

// first define a type-level function using TyFn
struct VecF;
impl<T> TyFn<T> for VecF { type Output = Vec<T>; }

// now we can convert from Vec<T> to Vec<U> as long as we have
// TyEq<T, U> as evidence
fn convert_vec<T, U>(eq: TyEq<T, U>, vec: Vec<T>) -> Vec<U> {
    eq.apply::<VecF>(vec)
}

Exchange T and U (symmetry).

Compose two equalities (transitivity).

impl<T, U> TyEq<T, U>
[src]

Cast from T to U.

Equivalent to .apply::<IdF>.

Trait Implementations

impl<T: ?Sized, U: ?Sized> Clone for TyEq<T, U>
[src]

Returns a copy of the value. Read more

Performs copy-assignment from source. Read more

impl<T: ?Sized, U: ?Sized> Copy for TyEq<T, U>
[src]

impl<T: ?Sized, U: ?Sized> Debug for TyEq<T, U>
[src]

Formats the value using the given formatter.