macro_rules! simple_type_witness {
(
$(#[$enum_meta:meta])*
$(derive($($derive:ident),* $(,)?))?
$(pub $(($($pub:tt)*))?)?
enum $enum:ident $($rem:tt)*
) => { ... };
}
Expand description
Declares a type witness enum.
A type witness is an enum whose variants only have TypeEq
fields.
Each variant requires the enum’s type parameter to be a specific type.
§Generated items
This macro always generates:
-
An enum with tuple variants, each of which has a single
TypeEq
field. -
An impl of
TypeWitnessTypeArg
for the enum. -
An impl of
MakeTypeWitness
for each variant of the enum.
Additional trait impls are generated when the derive(...)
syntax is used.
§Derivation
These impls are generated if you opt into them with the derive(...)
syntax:
Debug
PartialEq
Eq
PartialOrd
Ord
Hash
Equals
: pseudo-derive which is explained below
As opposed to #[derive(...))]
-generated implementations,
these impls don’t require type parameters to implement the derived trait.
This macro always implements Copy
and Clone
for the declared type witness,
derive(Copy, Clone)
does nothing.
§Equals derive
The Equals
derive adds this method to the generated witness enum:
pub const fn equals<__Wit2>(
self: WitnessEnum<__Wit>,
other: WitnessEnum<__Wit2>,
) -> typewit::TypeCmp<__Wit, __Wit2>
(TypeCmp<L, R>
is a witness of whether
L
and R
are the same or different types)
Limitations: this derive does not allow the type witness to:
- have any generic parameters
(aside from the implicitly declared
__Wit
type parameter) - have a
where
clause - have overriden generic arguments in the generated
MakeTypeWitness
impls
Examples:
§Syntax
This macro takes an enum-like syntax:
$(#[$enum_meta:meta])*
$(derive($($derive:ident),* $(,)?)))?
$vis:vis enum $enum:ident $(<$($generics:generics_param),* $(,)?>)?
$(where $($where:where_predicate),* $(,)? )?
{
$(
$(#[$variant_meta:meta])*
$variant:ident $(<$($var_gen_args:generic_arg)*>)?
// additional bounds for the MakeTypeWitness impl that constructs this variant.
$(where $($vari_where:where_predicate)*)?
// the type that this variant requires the
// implicit `__Wit` type parameter to be.
= $witnessed_ty:ty
),*
$(,)?
}
<$($var_gen_args:generic_arg)*>
(optional parameter)(example usage):
this parameter overrides the generic arguments of the enum in its
MakeTypeWitness
implementation.
derive($($derive:ident),* $(,)?)
(optional parameter)(example):
supports deriving the traits listed in the derivation section
#[cfg(...)]
attributes on variants are copied to their respective
MakeTypeWitness
impls.
Generic parameters support the #[cfg(...)]
attribute,
no other attribute is supported.
Defaults for generic parameters are only used
as the default value of $var_gen_args
(example usage) .
Soft-deprecated older syntax
This macro originally required the following syntax,
which is soft-deprecated, and will be supported for the rest of "1.*"
versions.
$(#[$enum_meta:meta])*
// Allows deriving some traits without the bounds that
// standard derives add to type parameters.
$(derive($($derive:ident),* $(,)?)))?
$vis:vis enum $enum:ident $([$($generics:tt)*])?
// The where clause of the enum
$(where[$($where:tt)*])?
{
$(
$(#[$variant_meta:meta])*
$variant:ident $([$($var_gen_args:tt)*])?
// additional bounds for the MakeTypeWitness impl that constructs this variant.
$(where[$($vari_where:tt)*])?
// the type this variant requires the implicit `__Wit` type parameter to be.
= $witnessed_ty:ty
),*
$(,)?
}
§Limitations
When used in Rust versions prior to 1.59.0,
type witnesses declared with this macro cannot have const parameters,
because this macro always adds a __Wit
type parameter after all generic parameters,
and those old versions don’t allow type parameters after const parameters.
§Examples
§Basic
This example demonstrates a basic usage of this macro
use typewit::MakeTypeWitness;
assert_eq!(do_it(1), 1);
assert_eq!(do_it(2), 4);
assert_eq!(do_it(3), 9);
assert_eq!(do_it("foo"), 3);
assert_eq!(do_it("hello"), 5);
const fn do_it<'a, T>(arg: T) -> usize
where
Witness<'a, T>: MakeTypeWitness,
{
match MakeTypeWitness::MAKE {
// `te` is a `TypeEq<T, u8>`, `te.to_right(arg)` goes from `T` to `u8.`
Witness::U8(te) => (te.to_right(arg) as usize).pow(2),
// `te` is a `TypeEq<T, &'a str>`, `te.to_right(arg)` goes from `T` to `&'a str.`
Witness::Str(te) => te.to_right(arg).len(),
}
}
typewit::simple_type_witness! {
// Declares an `enum Witness<'a, __Wit>`,
// the `__Wit` type parameter is added after all generics.
enum Witness<'a> {
// This variant requires `__Wit == u8`
U8 = u8,
// This variant requires `__Wit == &'a str`
Str = &'a str,
}
}
the above invocation of simple_type_witness
effectively generates this code:
enum Witness<'a, __Wit> {
U8(typewit::TypeEq<__Wit, u8>),
Str(typewit::TypeEq<__Wit, &'a str>),
}
impl<'a, __Wit> typewit::TypeWitnessTypeArg for Witness<'a, __Wit> {
type Arg = __Wit;
}
impl<'a> typewit::MakeTypeWitness for Witness<'a, u8> {
const MAKE: Self = Self::U8(typewit::TypeEq::NEW);
}
impl<'a> typewit::MakeTypeWitness for Witness<'a, &'a str> {
const MAKE: Self = Self::Str(typewit::TypeEq::NEW);
}
(consult the generated items section for all the generated impls)
§where clauses
This example demonstrates a variant with a where clause.
typewit::simple_type_witness! {
// Declares an `enum Witness<'a, T, __Wit>`,
// the `__Wit` type parameter is added after all generics.
#[non_exhaustive]
enum Witness<'a, T: 'a>
where
T: 'a + Debug
{
// This variant requires `__Wit == T`.
// The `MakeTypeWitness` impl for this variant also requires `T: Copy`.
#[cfg(feature = "foo")]
Value where T: Copy = T,
// This variant requires `__Wit == &'a T`
Ref = &'a T,
}
}
the above invocation of simple_type_witness
effectively generates this code:
#[non_exhaustive]
enum Witness<'a, T: 'a, __Wit: ?Sized>
where
T: 'a + Debug,
{
#[cfg(feature = "foo")]
Value(typewit::TypeEq<__Wit, T>),
Ref(typewit::TypeEq<__Wit, &'a T>),
}
impl<'a, T: 'a, __Wit: ?Sized> typewit::TypeWitnessTypeArg for Witness<'a, T, __Wit>
where
T: 'a + Debug,
{
type Arg = __Wit;
}
#[cfg(feature = "foo")]
impl<'a, T: 'a> typewit::MakeTypeWitness for Witness<'a, T, T>
where
T: 'a + Debug + Copy,
{
const MAKE: Self = Self::Value(typewit::TypeEq::NEW);
}
impl<'a, T: 'a> typewit::MakeTypeWitness for Witness<'a, T, &'a T>
where
T: 'a + Debug,
{
const MAKE: Self = Self::Ref(typewit::TypeEq::NEW);
}
(consult the generated items section for all the generated impls)
§$var_gen_args
parameter
This example shows what the $var_gen_args
parameter does,
as well as how generic parameter defaults relate to it.
(this example requires Rust 1.59.0)
typewit::simple_type_witness! {
// Declares an `enum Foo<T, const N: usize, __Wit>`,
// the `__Wit` type parameter is added after all generics.
//
// The defaults for generic parameters are only used
// as the default value of the generic arguments of variants.
enum Foo<T = i8, const N: usize = 1234> {
// This variant requires `__Wit == u64`.
//
// The `<(), 3>` here
// replaces `impl<T, const N: usize> MakeTypeWitness for Foo<T, N, u64>`
// with `impl MakeTypeWitness for Foo<(), 3, u64>`.
// Using `<(), 3>` allows the `T` and `N` type parameters to be inferred
// when the `MakeTypeWitness` impl for `Foo<_, _, u64>` is used.
U64<(), 3> = u64,
// This variant requires `__Wit == bool`.
//
// The `<>` here uses the defaults for the generic arguments to
// replace `impl<T, const N: usize> MakeTypeWitness for Foo<T, N, bool>`
// with `impl MakeTypeWitness for Foo<i8, 1234, bool>`.
Bool<> = bool,
// This variant requires `__Wit == [T; N]`.
Array = [T; N],
}
}
the above effectively expands to this:
enum Foo<T, const N: usize, __Wit: ?Sized> {
U64(typewit::TypeEq<__Wit, u64>),
Bool(typewit::TypeEq<__Wit, bool>),
Array(typewit::TypeEq<__Wit, [T; N]>),
}
impl<T, const N: usize, __Wit: ?Sized> typewit::TypeWitnessTypeArg for Foo<T, N, __Wit> {
type Arg = __Wit;
}
impl typewit::MakeTypeWitness for Foo<(), 3, u64> {
const MAKE: Self = Self::U64(typewit::TypeEq::NEW);
}
impl typewit::MakeTypeWitness for Foo<i8, 1234, bool> {
const MAKE: Self = Self::Bool(typewit::TypeEq::NEW);
}
impl<T, const N: usize> typewit::MakeTypeWitness for Foo<T, N, [T; N]> {
const MAKE: Self = Self::Array(typewit::TypeEq::NEW);
}
(consult the generated items section for all the generated impls)
§Derives
This example demonstrates derivation of all the supported traits
using the derive(...)
syntax (as opposed to the #[derive(...)]
attribute).
use typewit::{MakeTypeWitness, TypeEq};
struct NoImpls;
assert_eq!(Witness::<u8>::MAKE, Witness::<u8>::MAKE);
// Witness doesn't require its type parameters to impl any traits in its derives.
// The standard derives require that type parameters impl the derived trait,
// so this comparison wouldn't work (because `NoImpls` doesn't impl `PartialEq`).
assert_eq!(Witness::<NoImpls>::MAKE, Witness::NoImp(TypeEq::NEW));
typewit::simple_type_witness! {
// Declares an `enum Witness<__Wit>`,
// the `__Wit` type parameter is added after all generics.
derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Equals)
enum Witness {
U8 = u8,
NoImp = NoImpls,
}
}
§Deriving Equals
This example demonstrates basic usage of the Equals
derive
use typewit::{MakeTypeWitness, TypeCmp};
const _: () = {
let u8_wit: Witness<u8> = Witness::MAKE;
let u16_wit: Witness<u16> = Witness::MAKE;
let string_wit: Witness<String> = Witness::MAKE;
assert!(matches!(u8_wit.equals(u8_wit), TypeCmp::Eq(_)));
assert!(matches!(u8_wit.equals(u16_wit), TypeCmp::Ne(_)));
assert!(matches!(u8_wit.equals(string_wit), TypeCmp::Ne(_)));
assert!(matches!(u16_wit.equals(u8_wit), TypeCmp::Ne(_)));
assert!(matches!(u16_wit.equals(u16_wit), TypeCmp::Eq(_)));
assert!(matches!(u16_wit.equals(string_wit), TypeCmp::Ne(_)));
assert!(matches!(string_wit.equals(u8_wit), TypeCmp::Ne(_)));
assert!(matches!(string_wit.equals(u16_wit), TypeCmp::Ne(_)));
assert!(matches!(string_wit.equals(string_wit), TypeCmp::Eq(_)));
};
typewit::simple_type_witness! {
// Declares an `enum Witness<__Wit>`,
// the `__Wit` type parameter is added after all generics.
derive(Equals)
enum Witness {
U8 = u8,
U16 = u16,
String = String,
}
}
§Working around Equals
limitations
This example demonstrates how you can work around the “no generics, no where clause”
limitation of the Equals
derive to implement a generic function
that coerces equal types into arrays.
note: The reason simple_type_witness
does not do this implicitly is due to:
- the possibility of panics when there’s variants with the same witnessed type
- the need for additional bounds on the
equals
method - having to decide how to handle compatibility of witnesses with unequal generic arguments.
(this example requires Rust 1.61.0 due to use of trait bounds in const fns)
use typewit::{HasTypeWitness, MakeTypeWitness, TypeCmp};
// Cannot avoid having to specify the first generic arg here,
// because defaulting the `MakeTypeWitness` impl for `TypeWitness` over `u8` and `&str`
// (which would make the first argument of `pair_to_array` inferrable)
// makes their witnesses incompatible types with the witness for `&[T]`
// when they're passed to the `TypeWitness::equals` method.
assert_eq!(pair_to_array::<(), _, _>(3, 5), Ok([3, 5]));
assert_eq!(pair_to_array::<(), _, _>("hello", "world"), Ok(["hello", "world"]));
assert_eq!(pair_to_array(&[3u8, 5][..], &[8, 13][..]), Ok([&[3, 5][..], &[8, 13]]));
assert_eq!(pair_to_array::<(), _, _>("hello", 10), Err(("hello", 10)));
assert_eq!(pair_to_array(&[3u8, 5][..], 10), Err((&[3u8, 5][..], 10)));
pub const fn pair_to_array<'a, T: 'a, A, B>(foo: A, bar: B) -> Result<[A; 2], (A, B)>
where
A: Type<'a, T>,
B: Type<'a, T>,
{
// calls `TypeWitness::equals`
match A::WITNESS.equals(B::WITNESS) {
// te: TypeEq<A, B>, a value-level proof that A == B
TypeCmp::Eq(te) => {
// `te.to_left(bar)` here coerces `bar` from `B` to `A`
Ok([foo, te.to_left(bar)])
}
// _ne: TypeNe<A, B>, a value-level proof that A != B
TypeCmp::Ne(_ne) => Err((foo, bar)),
}
}
impl<'a, T, Wit: HasTypeKind> TypeWitness<'a, T, Wit> {
/// Compares `Wit` and Wit2` for equality,
/// returning a proof of their (in)equality.
pub const fn equals<Wit2: HasTypeKind>(
self,
other: TypeWitness<'a, T, Wit2>,
) -> TypeCmp<Wit, Wit2> {
match (self, other) {
// `Wit == u8` and `Wit2 == u8`, therefore Wit == Wit2
(TypeWitness::U8(l_te), TypeWitness::U8(r_te)) => {
l_te.join(r_te.flip()).to_cmp()
}
// `Wit == &'a str` and `Wit2 == &'a str`, therefore Wit == Wit2
(TypeWitness::Str(l_te), TypeWitness::Str(r_te)) => {
l_te.join(r_te.flip()).to_cmp()
}
// `Wit == &'a [T]` and `Wit2 == &'a [T]`, therefore Wit == Wit2
(TypeWitness::Slice(l_te), TypeWitness::Slice(r_te)) => {
l_te.join(r_te.flip()).to_cmp()
}
// the witnesses aren't the same variant, so they're different types
// (requires the witness to be declared with never-overlapping variants)
//
// using this instead of a blanket `_ => ` pattern because,
// with a `_` pattern, adding more variants would run this branch,
// causing a panic when those types are compared to themselves.
|(TypeWitness::U8(_), _)
|(TypeWitness::Str(_), _)
|(TypeWitness::Slice(_), _)
=> {
// Compares the `HasTypeKind::Kind` assoc types of `Wit` and `Wit2`
// with `.equals()`, unwraps into a proof of `Wit::Kind != Wit2::Kind`,
// then maps that proof to one of `Wit != Wit2`.
//
// caveat: if any of the variants holds a type witness with the same
// `HasTypeKind::Kind` as another,
// then comparing those variants will cause `unwrap_ne` to panic,
Wit::KIND_WITNESS.equals(Wit2::KIND_WITNESS)
.unwrap_ne()
.map_to_arg(TypeKindFn) // maps from kind to type
.to_cmp()
}
}
}
}
typewit::simple_type_witness! {
// Declares an `enum TypeWitness<'a, T: 'a, __Wit>`,
// the `__Wit` type parameter is added after all generics.
pub enum TypeWitness<'a, T: 'a> {
U8 = u8,
Str = &'a str,
Slice = &'a [T],
}
}
// emulates trait alias for `HasTypeKind + HasTypeWitness<TypeWitness<'a, T, Self>>`
pub trait Type<'a, T: 'a>: HasTypeKind + HasTypeWitness<TypeWitness<'a, T, Self>> {}
impl<'a, T: 'a, Self_> Type<'a, T> for Self_
where
Self_: HasTypeKind + HasTypeWitness<TypeWitness<'a, T, Self>>
{}
// Giving each type an associated kind, kinds have their own type witness.
pub trait HasTypeKind {
type Kind: HasTypeWitness<KindWitness<Self::Kind>>;
// helper for getting the type witness of the `Kind` associated type
const KIND_WITNESS: KindWitness<Self::Kind> = <Self::Kind>::WITNESS;
}
// defining a type-level function from a type to its associated kind,
// used by `.map_to_arg` above to do the inverse (going from kind to type).
typewit::type_fn!{
struct TypeKindFn;
impl<This: HasTypeKind> This => This::Kind
}
pub struct U8Kind;
impl HasTypeKind for u8 {
type Kind = U8Kind;
}
pub struct StrKind;
impl HasTypeKind for &str {
type Kind = StrKind;
}
pub struct SliceKind;
impl<T> HasTypeKind for &[T] {
type Kind = SliceKind;
}
typewit::simple_type_witness! {
// Declares an `enum KindWitness<__Wit>`, type witness for the kinds
derive(Equals)
pub enum KindWitness {
U8 = U8Kind,
Str = StrKind,
Slice = SliceKind,
}
}