pub struct TypeEq<L: ?Sized, R: ?Sized>(/* private fields */);
Expand description
Value-level proof that L
is the same type as R
This type can be used to prove that L
and R
are the same type,
because it can only be safely constructed with
TypeEq::<L, L>::NEW
(or new
),
where both type arguments are the same type.
This type is not too useful by itself, it becomes useful when put inside of an enum.
TypeEq<L, R>
uses the L
type parameter as the more generic type by convention
(e.g: TypeEq<T, char>
).
This only matters if you’re using the type witness traits
(HasTypeWitness
,
MakeTypeWitness
,
TypeWitnessTypeArg
) with TypeEq
.
Soundness
TypeEq<L, R>
requires both type arguments to be the same type so that
projecting the type arguments results in the same type for
both arguments.
Unsafely creating a TypeEq<L, R>
where L != R
allows
transmuting between any two types
(that is bad).
Examples
Polymorphic branching
This example demonstrates how type witnesses can be used to choose between expressions of different types with a constant.
use typewit::TypeEq;
const fn main() {
assert!(matches!(choose!(0; b"a string", 2, panic!()), b"a string"));
const UNO: u64 = 1;
assert!(matches!(choose!(UNO; loop{}, [3, 5], true), [3, 5]));
assert!(matches!(choose!(2 + 3; (), unreachable!(), ['5', '3']), ['5', '3']));
}
/// Evaluates the argument at position `$chosen % 3`, other arguments aren't evaluated.
///
/// The arguments can all be different types.
///
/// `$chosen` must be a `u64` constant.
#[macro_export]
macro_rules! choose {
($chosen:expr; $arg_0: expr, $arg_1: expr, $arg_2: expr) => {
match Choice::<{$chosen % 3}>::VAL {
// `te` (a `TypeEq<T, X>`) allows us to safely go between
// the type that the match returns (its `T` type argument)
// and the type of `$arg_0` (its `X` type argument).
Branch3::A(te) => {
// `to_left` goes from `X` to `T`
te.to_left($arg_0)
}
// same as the `A` branch, with a different type for the argument
Branch3::B(te) => te.to_left($arg_1),
// same as the `A` branch, with a different type for the argument
Branch3::C(te) => te.to_left($arg_2),
}
}
}
// This is a type witness
pub enum Branch3<T, X, Y, Z> {
// This variant requires `T == X`
A(TypeEq<T, X>),
// This variant requires `T == Y`
B(TypeEq<T, Y>),
// This variant requires `T == Z`
C(TypeEq<T, Z>),
}
// Used to get different values of `Branch3` depending on `N`
pub trait Choice<const N: u64> {
const VAL: Self;
}
impl<X, Y, Z> Choice<0> for Branch3<X, X, Y, Z> {
// Because the first two type arguments of `Branch3` are `X`
// (as required by the `TypeEq<T, X>` field in Branch3's type definition),
// we can use `TypeEq::NEW` here.
const VAL: Self = Self::A(TypeEq::NEW);
}
impl<X, Y, Z> Choice<1> for Branch3<Y, X, Y, Z> {
const VAL: Self = Self::B(TypeEq::NEW);
}
impl<X, Y, Z> Choice<2> for Branch3<Z, X, Y, Z> {
const VAL: Self = Self::C(TypeEq::NEW);
}
Implementations§
source§impl<T: ?Sized> TypeEq<T, T>
impl<T: ?Sized> TypeEq<T, T>
sourcepub const NEW: Self = _
pub const NEW: Self = _
Constructs a TypeEq<T, T>
.
Example
use typewit::TypeEq;
assert_eq!(mutate(5, Wit::U32(TypeEq::NEW)), 25);
assert_eq!(mutate(5, Wit::Other(TypeEq::NEW)), 5);
assert_eq!(mutate("hello", Wit::Other(TypeEq::NEW)), "hello");
const fn mutate<W>(val: W, wit: Wit<W>) -> W {
match wit {
Wit::U32(te) => te.to_left(te.to_right(val) + 20),
Wit::Other(_) => val,
}
}
// This can't be written using the `simple_type_witness` macro because the
// type in the `Other` variant overlaps with the other ones.
enum Wit<W> {
U32(TypeEq<W, u32>),
Other(TypeEq<W, W>),
}
source§impl<L: ?Sized, R: ?Sized> TypeEq<L, R>
impl<L: ?Sized, R: ?Sized> TypeEq<L, R>
sourcepub const unsafe fn new_unchecked() -> TypeEq<L, R>
pub const unsafe fn new_unchecked() -> TypeEq<L, R>
Constructs a TypeEq<L, R>
.
Safety
You must ensure that L
is the same type as R
.
Examples
Unsound usage
This example demonstrates why L == R
is a strict requirement.
use typewit::{TypeEq, TypeFn};
// SAFETY: WRONG! UNSOUND!
let te: TypeEq<u8, i8> = unsafe{ TypeEq::new_unchecked() };
// Because `TypeEq<u8, i8>` is incorrect,
// we get this absurd `TypeEq` from the `project` method.
let absurd: TypeEq<(), Vec<usize>> = te.project::<Func>();
// This casts from `()` to `Vec<usize>` (which is UB).
// Last time I tried uncommenting this, it killed the test runner.
// absurd.to_right(());
struct Func;
impl TypeFn<u8> for Func { type Output = (); }
impl TypeFn<i8> for Func { type Output = Vec<usize>; }
sourcepub const fn flip(self: TypeEq<L, R>) -> TypeEq<R, L>
pub const fn flip(self: TypeEq<L, R>) -> TypeEq<R, L>
Swaps the type parameters of this TypeEq
Example
use typewit::TypeEq;
assert_eq!(flip_bytes([3, 5], TypeEq::NEW), [5, 3]);
const fn flip_bytes<T>(val: T, te: TypeEq<T, [u8; 2]>) -> T {
bar(val, te.flip())
}
const fn bar<T>(val: T, te: TypeEq<[u8; 2], T>) -> T {
let [l, r] = te.to_left(val);
te.to_right([r, l])
}
sourcepub const fn join<O: ?Sized>(
self: TypeEq<L, R>,
_other: TypeEq<R, O>
) -> TypeEq<L, O>
pub const fn join<O: ?Sized>( self: TypeEq<L, R>, _other: TypeEq<R, O> ) -> TypeEq<L, O>
Joins this TypeEq<L, R>
with a TypeEq<R, O>
, producing a TypeEq<L, O>
.
The returned TypeEq
can then be used to coerce between L
and O
.
Example
use typewit::TypeEq;
assert_eq!(foo(TypeEq::NEW, TypeEq::NEW, Some(3)), Some(3));
assert_eq!(foo(TypeEq::NEW, TypeEq::NEW, None), None);
fn foo<L, X>(
this: TypeEq<L, Option<X>>,
that: TypeEq<Option<X>, Option<u32>>,
value: Option<u32>,
) -> L {
let te: TypeEq<L, Option<u32>> = this.join(that);
te.to_left(value)
}
source§impl<L0, R0> TypeEq<L0, R0>
impl<L0, R0> TypeEq<L0, R0>
sourcepub const fn zip<L1, R1>(
self: TypeEq<L0, R0>,
other: TypeEq<L1, R1>
) -> TypeEq<(L0, L1), (R0, R1)>
pub const fn zip<L1, R1>( self: TypeEq<L0, R0>, other: TypeEq<L1, R1> ) -> TypeEq<(L0, L1), (R0, R1)>
Combines this TypeEq<L0, R0>
with a TypeEq<L1, R1>
,
producing a TypeEq<(L0, L1), (R0, R1)>
.
Example
This example demonstrates how one can combine two TypeEq
s to use
with a multi-parameter type.
This example requires the "const_marker"
feature (enabled by default)
because it uses Usize
use typewit::{const_marker::Usize, TypeEq, TypeFn};
assert_eq!(make_foo(TypeEq::NEW, TypeEq::NEW), Foo("hello", [3, 5, 8]));
const fn make_foo<T, const N: usize>(
te_ty: TypeEq<T, &'static str>,
te_len: TypeEq<Usize<N>, Usize<3>>,
) -> Foo<T, N> {
// the type annotations are just for the reader, they can be inferred.
let te_pair: TypeEq<(T, Usize<N>), (&str, Usize<3>)> = te_ty.zip(te_len);
let te: TypeEq<Foo<T, N>, Foo<&str, 3>> = te_pair.project::<GFoo>();
// `te.to_left(...)` here goes from `Foo<&str, 3>` to `Foo<T, N>`
te.to_left(Foo("hello", [3, 5, 8]))
}
#[derive(Debug, PartialEq)]
struct Foo<T, const N: usize>(T, [u8; N]);
typewit::type_fn!{
// Type-level function from `(T, Usize<N>)` to `Foo<T, N>`
struct GFoo;
impl<T, const N: usize> (T, Usize<N>) => Foo<T, N>
}
sourcepub const fn zip3<L1, R1, L2, R2>(
self: TypeEq<L0, R0>,
other1: TypeEq<L1, R1>,
other2: TypeEq<L2, R2>
) -> TypeEq<(L0, L1, L2), (R0, R1, R2)>
pub const fn zip3<L1, R1, L2, R2>( self: TypeEq<L0, R0>, other1: TypeEq<L1, R1>, other2: TypeEq<L2, R2> ) -> TypeEq<(L0, L1, L2), (R0, R1, R2)>
Combines three TypeEq<L*, R*>
to produce a
TypeEq<(L0, L1, L2), (R0, R1, R2)>
.
Example
use typewit::{TypeEq, type_eq};
use std::cmp::Ordering::{self, Less};
assert_eq!(make_tuple(type_eq(), type_eq(), type_eq()), (3, "foo", Less));
fn make_tuple<A, B, C>(
te0: TypeEq<A, u8>,
te1: TypeEq<B, &str>,
te2: TypeEq<C, Ordering>,
) -> (A, B, C) {
te0.zip3(te1, te2) // returns `TypeEq<(A, B, C), (u8, &str, Ordering)>`
.to_left((3, "foo", Less))
}
sourcepub const fn zip4<L1, R1, L2, R2, L3, R3>(
self: TypeEq<L0, R0>,
other1: TypeEq<L1, R1>,
other2: TypeEq<L2, R2>,
other3: TypeEq<L3, R3>
) -> TypeEq<(L0, L1, L2, L3), (R0, R1, R2, R3)>
pub const fn zip4<L1, R1, L2, R2, L3, R3>( self: TypeEq<L0, R0>, other1: TypeEq<L1, R1>, other2: TypeEq<L2, R2>, other3: TypeEq<L3, R3> ) -> TypeEq<(L0, L1, L2, L3), (R0, R1, R2, R3)>
Combines four TypeEq<L*, R*>
to produce a
TypeEq<(L0, L1, L2, L3), (R0, R1, R2, L3)>
.
Example
use typewit::{TypeEq, type_eq};
use std::cmp::Ordering::{self, Less};
assert_eq!(
make_tuple(type_eq(), type_eq(), type_eq(), type_eq()),
(3, "foo", Less, true),
);
fn make_tuple<A, B, C, D>(
te0: TypeEq<A, u8>,
te1: TypeEq<B, &str>,
te2: TypeEq<C, Ordering>,
te3: TypeEq<D, bool>,
) -> (A, B, C, D) {
let te: TypeEq<(A, B, C, D), (u8, &str, Ordering, bool)> = te0.zip4(te1, te2, te3);
te.to_left((3, "foo", Less, true))
}
source§impl<L, R> TypeEq<L, R>
impl<L, R> TypeEq<L, R>
sourcepub const fn reachability_hint<T>(self, val: T) -> T
pub const fn reachability_hint<T>(self, val: T) -> T
Hints to the compiler that a TypeEq<L, R>
can only be constructed if L == R
.
This function takes and returns val
unmodified.
This allows returning some value from an expression
while hinting that L == R
.
sourcepub const fn to_right(self, from: L) -> R
pub const fn to_right(self, from: L) -> R
A no-op cast from L
to R
.
This cast is a no-op because having a TypeEq<L, R>
value
proves that L
and R
are the same type.
Example
use typewit::{TypeEq, type_eq};
use std::cmp::Ordering::{self, *};
assert_eq!(mutated(Less, Wit::Ord(type_eq())), Greater);
assert_eq!(mutated(Equal, Wit::Ord(type_eq())), Equal);
assert_eq!(mutated(Greater, Wit::Ord(type_eq())), Less);
assert_eq!(mutated(false, Wit::Bool(type_eq())), true);
assert_eq!(mutated(true, Wit::Bool(type_eq())), false);
const fn mutated<R>(arg: R, w: Wit<R>) -> R {
match w {
Wit::Ord(te) => te.to_left(te.to_right(arg).reverse()),
Wit::Bool(te) => te.to_left(!te.to_right(arg)),
}
}
enum Wit<R> {
Ord(TypeEq<R, Ordering>),
Bool(TypeEq<R, bool>),
}
sourcepub const fn to_left(self, from: R) -> L
pub const fn to_left(self, from: R) -> L
A no-op cast from R
to L
.
This cast is a no-op because having a TypeEq<L, R>
value
proves that L
and R
are the same type.
Example
use typewit::{TypeEq, type_eq};
assert_eq!(stuff(Wit::OptSlice(type_eq())), Some(&[3, 5, 8][..]));
assert_eq!(stuff(Wit::Bool(type_eq())), true);
const fn stuff<R>(te: Wit<R>) -> R {
match te {
Wit::OptSlice(te) => te.to_left(Some(&[3, 5, 8])),
Wit::Bool(te) => te.to_left(true),
}
}
enum Wit<R> {
OptSlice(TypeEq<R, Option<&'static [u16]>>),
Bool(TypeEq<R, bool>),
}
source§impl<L: ?Sized, R: ?Sized> TypeEq<L, R>
impl<L: ?Sized, R: ?Sized> TypeEq<L, R>
sourcepub const fn map<F>(self, func: F) -> TypeEq<CallFn<F, L>, CallFn<F, R>>where
F: TypeFn<L> + TypeFn<R>,
pub const fn map<F>(self, func: F) -> TypeEq<CallFn<F, L>, CallFn<F, R>>where F: TypeFn<L> + TypeFn<R>,
Maps the type arguments of this TypeEq
by using the F
type-level function.
Use this function over project
if you want the type of the passed in function to be inferred.
Example
use typewit::{TypeEq, TypeFn};
assert_eq!(foo(TypeEq::NEW), (false, 5));
const fn foo<'a, T>(te: TypeEq<u32, T>) -> (bool, T) {
// `GPair<bool>` maps `u32` to `(bool, u32)`
// and maps `T` to `(bool, T)`
let map_te: TypeEq<(bool, u32), (bool, T)> = te.map(GPair::<bool>::NEW);
// same as the above, but inferring `GPair`'s generic arguments.
let _: TypeEq<(bool, u32), (bool, T)> = te.map(GPair::NEW);
map_te.to_right((false, 5u32))
}
// Declares `struct GPair<A>`, a type-level function from `B` to `(A, B)`
typewit::type_fn! {
struct GPair<A>;
impl<B> B => (A, B)
}
sourcepub const fn project<F>(self) -> TypeEq<CallFn<F, L>, CallFn<F, R>>where
F: TypeFn<L> + TypeFn<R>,
pub const fn project<F>(self) -> TypeEq<CallFn<F, L>, CallFn<F, R>>where F: TypeFn<L> + TypeFn<R>,
Maps the type arguments of this TypeEq
by using the F
type-level function.
Use this function over map
if you want to specify the type of the passed in function explicitly.
Example
use typewit::{TypeEq, TypeFn};
assert_eq!(foo(TypeEq::NEW), vec![3u32, 5, 8]);
fn foo<T>(te: TypeEq<u32, T>) -> Vec<T> {
let vec_te: TypeEq<Vec<u32>, Vec<T>> = te.project::<GVec>();
vec_te.to_right(vec![3, 5, 8])
}
// Declares `GVec`, a type-level function from `T` to `Vec<T>`
typewit::type_fn!{
struct GVec;
impl<T> T => Vec<T>
}
sourcepub const fn in_ref<'a>(self) -> TypeEq<&'a L, &'a R>
pub const fn in_ref<'a>(self) -> TypeEq<&'a L, &'a R>
Converts a TypeEq<L, R>
to TypeEq<&L, &R>
Example
use typewit::{MakeTypeWitness, TypeEq};
assert_eq!(get::<u8>(), &3);
assert_eq!(get::<str>(), "hello");
const fn get<R: ?Sized>() -> &'static R
where
Returned<R>: MakeTypeWitness
{
match MakeTypeWitness::MAKE {
// `te` is a `TypeEq<R, u8>`
Returned::U8(te) => te.in_ref().to_left(&3),
// `te` is a `TypeEq<R, str>`
Returned::Str(te) => te.in_ref().to_left("hello"),
}
}
typewit::simple_type_witness! {
// declares the `enum Returned<R> {` type witness
enum Returned {
// this variant requires `R == u8`
U8 = u8,
// this variant requires `R == str`
Str = str,
}
}
sourcepub const fn in_mut<'a>(self) -> TypeEq<&'a mut L, &'a mut R>
pub const fn in_mut<'a>(self) -> TypeEq<&'a mut L, &'a mut R>
Converts a TypeEq<L, R>
to TypeEq<&mut L, &mut R>
Constness
This requires either of the "mut_refs"
or "const_mut_refs"
crate features to be enabled to be a const fn
.
Example
Because this example calls in_mut
inside a const fn
,
it requires either of the "mut_refs"
or "nightly_mut_refs"
crate features.
use typewit::{TypeEq, type_eq};
let foo = &mut Foo { bar: 10, baz: ['W', 'H', 'O'] };
*get_mut(foo, Field::Bar(type_eq())) *= 2;
assert_eq!(foo.bar, 20);
assert_eq!(*get_mut(foo, Field::Baz(type_eq())), ['W', 'H', 'O']);
const fn get_mut<R>(foo: &mut Foo, te: Field<R>) -> &mut R {
match te {
Field::Bar(te) => te.in_mut().to_left(&mut foo.bar),
Field::Baz(te) => te.in_mut().to_left(&mut foo.baz),
}
}
struct Foo {
bar: u8,
baz: [char; 3],
}
enum Field<R: ?Sized> {
Bar(TypeEq<R, u8>),
Baz(TypeEq<R, [char; 3]>),
}
sourcepub const fn in_box(self) -> TypeEq<Box<L>, Box<R>>
Available on crate feature alloc
only.
pub const fn in_box(self) -> TypeEq<Box<L>, Box<R>>
alloc
only.Converts a TypeEq<L, R>
to TypeEq<Box<L>, Box<R>>
Example
use typewit::{MakeTypeWitness, TypeEq, type_eq};
use std::any::Any;
use std::fmt::Display;
assert_eq!(factory::<dyn Any>().downcast::<u16>().unwrap(), Box::new(1337));
assert_eq!(factory::<dyn Display>().to_string(), "hello bob");
fn factory<R: ?Sized>() -> Box<R>
where
Dyn<R>: MakeTypeWitness
{
match MakeTypeWitness::MAKE {
// `te` is a `TypeEq<R, dyn Any>`
Dyn::Any(te) => te.in_box().to_left(Box::new(1337u16)),
// `te` is a `TypeEq<R, dyn Display>`
Dyn::Display(te) => te.in_box().to_left(Box::new("hello bob")),
}
}
typewit::simple_type_witness! {
// declares the `enum Dyn<R> {` type witness
enum Dyn {
// this variant requires `R == dyn Any`
Any = dyn Any,
// this variant requires `R == dyn Display`
Display = dyn Display,
}
}
source§impl<L: Sized, R: Sized> TypeEq<L, R>
impl<L: Sized, R: Sized> TypeEq<L, R>
sourcepub const fn in_array<const UL: usize, const UR: usize>(
self,
other: TypeEq<Usize<UL>, Usize<UR>>
) -> TypeEq<[L; UL], [R; UR]>
Available on crate feature const_marker
only.
pub const fn in_array<const UL: usize, const UR: usize>( self, other: TypeEq<Usize<UL>, Usize<UR>> ) -> TypeEq<[L; UL], [R; UR]>
const_marker
only.Combines TypeEq<L, R>
and TypeEq<Usize<UL>, Usize<UR>>
into TypeEq<[L; UL], [R; UR]>
Example
motivation
The safe way to map an array in const fns(on stable Rust in 2023) is to create an array of the returned type with some dummy value, and then fill it in with the desired values.
Because the function in this example takes a [T; LEN]
where the T
is generic,
it copies the first element of the input array to initialize the returned array,
so we must handle empty arrays,
but trying to return an empty array the naive way
if LEN == 0 {
return [];
}
does not work
error[E0308]: mismatched types
--> src/type_eq.rs:827:16
|
4 | const fn map_wrapping<T: Copy, const LEN: usize>(arr: [T; LEN]) -> [Wrapping<T>; LEN] {
| ------------------ expected `[Wrapping<T>; LEN]` because of return type
5 | if LEN == 0 {
6 | return [];
| ^^ expected `LEN`, found `0`
|
= note: expected array `[Wrapping<T>; LEN]`
found array `[_; 0]`
This example demonstrates how in_array
allows one to return an empty array:
(this example requires Rust 1.61.0, because it uses trait bounds in const fns)
use typewit::{const_marker::Usize, TypeEq};
use std::num::Wrapping;
assert_eq!(map_wrapping([""; 0]), []);
assert_eq!(map_wrapping([3, 5, 8]), [Wrapping(3), Wrapping(5), Wrapping(8)]);
const fn map_wrapping<T: Copy, const LEN: usize>(arr: [T; LEN]) -> [Wrapping<T>; LEN] {
// `teq` is a `TypeEq<Usize<LEN>, Usize<0>>`
if let Ok(teq) = Usize::<LEN>.eq(Usize::<0>) {
return TypeEq::new::<Wrapping<T>>()
.in_array(teq) // `TypeEq<[Wrapping<T>; LEN], [Wrapping<T>; 0]>`
.to_left([]);
}
let mut ret = [Wrapping(arr[0]); LEN];
let mut i = 1;
while i < LEN {
ret[i] = Wrapping(arr[i]);
i += 1;
}
ret
}
Trait Implementations§
source§impl<T: ?Sized> MakeTypeWitness for TypeEq<T, T>
impl<T: ?Sized> MakeTypeWitness for TypeEq<T, T>
source§impl<L: ?Sized, R: ?Sized> Ord for TypeEq<L, R>
impl<L: ?Sized, R: ?Sized> Ord for TypeEq<L, R>
source§impl<L: ?Sized, R: ?Sized> PartialOrd<TypeEq<L, R>> for TypeEq<L, R>
impl<L: ?Sized, R: ?Sized> PartialOrd<TypeEq<L, R>> for TypeEq<L, R>
1.0.0 · source§fn le(&self, other: &Rhs) -> bool
fn le(&self, other: &Rhs) -> bool
self
and other
) and is used by the <=
operator. Read more