typewit

Macro simple_type_witness

Source
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.

examples below

§Generated items

This macro always generates:

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

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.

§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)
    enum Witness {
        U8 = u8,
        NoImp = NoImpls,
    }
}