Macro typewit::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.
§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
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,
}
}