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
andB
are truly transmute-compatible (which usually meansA
andB
must differ only in phantom parameters), and- changing from
A
toB
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]
fn refl() -> Self
Constructor for TyEq
(reflexivity).
impl<T: ?Sized, U: ?Sized> TyEq<T, U>
[src]
fn apply<F: ?Sized>(
self,
value: <F as TyFn<T>>::Output
) -> <F as TyFn<U>>::Output where
F: TyFn<T> + TyFn<U>,
<F as TyFn<T>>::Output: Sized,
<F as TyFn<U>>::Output: Sized,
self,
value: <F as TyFn<T>>::Output
) -> <F as TyFn<U>>::Output where
F: TyFn<T> + TyFn<U>,
<F as TyFn<T>>::Output: Sized,
<F as TyFn<U>>::Output: Sized,
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) }
fn sym(self) -> TyEq<U, T>
Exchange T
and U
(symmetry).
fn trans<R: ?Sized>(self, other: TyEq<U, R>) -> TyEq<T, R>
Compose two equalities (transitivity).
impl<T, U> TyEq<T, U>
[src]
Trait Implementations
impl<T: ?Sized, U: ?Sized> Clone for TyEq<T, U>
[src]
fn clone(&self) -> Self
Returns a copy of the value. Read more
fn clone_from(&mut self, source: &Self)
1.0.0
Performs copy-assignment from source
. Read more