This crate provides abstractions for creating type witnesses.
The inciting motivation for this crate is emulating trait polymorphism in const fn
(as of 2025-07-20, it's not possible to call trait methods in const contexts on stable).
What are type witnesses
Type witnesses are values that prove the equality of a type parameter to a fixed set of possible types.
The simplest type witness is TypeEq<L, R>,
which only proves equality of its L and R type parameters,
and can be used to coerce between them.
Most type witnesses are enums with TypeEq fields,
which can coerce between a type parameter and as many types as there are variants.
Examples
Polymorphic function
This demonstrates how one can write a polymorphic const fn
(as of 2025-07-20, trait methods can't be called in const fns on stable)
(this example requires Rust 1.61.0, since it uses trait bounds in const)
use ;
const VALS: = ;
assert_eq!;
// A "method" of the `Message` trait (declared below)
const
// The trait that we use to emulate polymorphic dispatch,
// the limitation is that it can only emulate it for a limited set of types known
// to the crate that defines the trait, in this case that's `usize` and `&str`.
// replacing these impls with a blanket impl leads to worse compilation errors
// This macro declares `enum MessageWitness<'a, __Wit>`, a type witness enum,
// where each variant requires and then guarantees `__Wit` to be a particular type.
// (the `__Wit` type parameter is implicitly added after all generics)
simple_type_witness!
Indexing polymorphism
This function demonstrates const fn polymorphism
and projecting TypeEq by implementing TypeFn.
(this example requires Rust 1.71.0, because it uses <[T]>::split_at in a const context.
use Range;
use ;
const
// This macro declares a type witness enum
simple_type_witness!
/// Trait for all types that can be used as slice indices
///
/// The `HasTypeWitness` supertrait allows getting a `IndexWitness<Self>`
/// with its `WITNESS` associated constant.
type SliceIndexRet<I, T> = Returns;
// Declares `struct FnSliceIndexRet<T>`
// a type-level function (TypeFn implementor) from `I` to `SliceIndexRet<I, T>`
type_fn!
When the wrong type is passed for the index, the compile-time error is the same as with normal generic functions:
error[E0277]: the trait bound `RangeFull: SliceIndex<{integer}>` is not satisfied
--> src/main.rs:43:30
|
13 | assert_eq!(index(&array, ..), [13, 21]);
| ----- ^^ the trait `SliceIndex<{integer}>` is not implemented for `RangeFull`
| |
| required by a bound introduced by this call
|
= help: the following other types implement trait `SliceIndex<T>`:
std::ops::Range<usize>
usize
Downcasting const generic type
This example demonstrates "downcasting" from a type with a const parameter to a concrete instance of that type.
use ;
assert_eq!;
assert_eq!;
assert_eq!;
assert_eq!; // this is different!
assert_eq!;
;
// Declares `struct GArr`
// a type-level function (TypeFn implementor) from `Usize<N>` to `Arr<N>`
type_fn!
Builder
Using a type witness to help encode a type-level enum, and to match on that type-level enum inside of a function.
The type-level enum is used to track the initialization of fields in a builder.
This example requires Rust 1.65.0, because it uses Generic Associated Types.
use HasTypeWitness;
// Emulates a type-level `enum InitState { Init, Uninit }`
// If `I` is `Uninit`, then this evaluates to `()`
// If `I` is `Init`, then this evaluates to `T`
type BuilderField<I, T> = ;
/// Gets `T` out of `maybe_init` if it's actually initialized,
/// otherwise returns `else_()`.
Generic Const Expressions
This example uses Usize to coerce an arrays whose length is generic to
another generic, but equal, length.
This example requires the "generic_const_exprs" crate feature because it uses the
currently-unstable generic_const_exprs language feature.
use ;
let mut arrays = ;
arrays.swap_inner;
assert_eq!;
assert_eq!;
; A * B]:,
:,
const >, B * A}>>
>.equals.unwrap_eq
}
If you tried to swap the fields directly, you'd get this error:
error[E0308]: mismatched types
--> src/lib.rs:437:38
|
42 | core::mem::swap(&mut self.a, &mut self.b);
| ^^^^^^^^^^^ expected `A * B`, found `B * A`
|
= note: expected constant `A * B`
found constant `B * A`
Cargo features
These are the features of this crate.
Default-features
These features are enabled by default:
"proc_macros": uses proc macros to improve compile-errors involving macro-generated impls.
Rust-versions and standard crates
These features enable items that have a minimum Rust version:
-
"rust_stable": enables all the"rust_1_*"features. -
"rust_1_83": turns functions that take mutable references intoconst fns, enablesconst_markeritems for comparingConstMarkers, and enables the"rust_1_65"feature. -
"rust_1_65": enables thetype_constructorsmodule, themethodsmodule, and the"rust_1_61"feature. -
"rust_1_61": enablesMetaBaseTypeWit,BaseTypeWitness, and the{TypeCmp, TypeNe}::{zip*, in_array}methods.
These features enable items that require a non-core standard crate:
"alloc": enable items that use anything from the standardalloccrate.
Nightly features
These features require the nightly Rust compiler:
-
"adt_const_marker": enables the"rust_stable"crate feature, and marker types in theconst_markermodule that have non-primitiveconstparameters. -
"generic_const_exprs": enables the"rust_stable"crate feature, and doc examples that use thegeneric_const_exprsunstable language feature.
No-std support
typewit is #![no_std], it can be used anywhere Rust can be used.
You need to enable the "alloc" feature to enable items that use anything
from the standard alloc crate.
Minimum Supported Rust Version
typewit supports Rust 1.57.0.
Features that require newer versions of Rust, or the nightly compiler, need to be explicitly enabled with crate features.