11 个稳定版本
1.9.0 | 2024年1月6日 |
---|---|
1.8.0 | 2023年10月5日 |
1.7.0 | 2023年9月10日 |
1.5.0 | 2023年7月23日 |
0.0.1 | 2023年4月23日 |
#151 in Rust 模式
230,457 每月下载量
用于 65 个 包(直接使用 6 个)
290KB
4K SLoC
此包提供创建 类型见证 的抽象。
此包的激发动机是在 const fn
中模拟特例多态(截至 2023-10-01,在稳定版本中无法在 const 环境中调用特例方法)。
什么是类型见证
类型见证是枚举,允许在类型参数和一系列可能类型(每个变体一个)之间进行强制转换。
最简单的类型见证是 TypeEq<L, R>
,它只允许在 L
和 R
之间进行强制转换。
大多数类型见证都是具有 TypeEq
字段的枚举,可以在类型参数和变体数量一样多的类型之间进行强制转换。
示例
多态函数
这演示了如何编写返回类型多态的 const fn
(截至 2023-10-01,在稳定版本中无法在 const fn 中调用特例方法)
use typewit::{MakeTypeWitness, TypeEq};
assert_eq!(returnal::<u8>(), 3);
assert_eq!(returnal::<&str>(), "hello");
const fn returnal<'a, R>() -> R
where
RetWitness<'a, R>: MakeTypeWitness,
{
match MakeTypeWitness::MAKE {
RetWitness::U8(te) => {
// `te` (a `TypeEq<R, u8>`) allows coercing between `R` and `u8`,
// because `TypeEq` is a value-level proof that both types are the same.
// `te.to_left(...)` goes from `u8` to `R`.
te.to_left(3u8)
}
RetWitness::Str(te) => {
// `te` is a `TypeEq<R, &'a str>`
// `te.to_left(...)` goes from `&'a str` to `R`.
te.to_left("hello")
}
}
}
// This macro declares a type witness enum
typewit::simple_type_witness! {
// Declares `enum RetWitness<'a, __Wit>`
// (the `__Wit` type parameter is implicitly added after all generics)
enum RetWitness<'a> {
// This variant requires `__Wit == u8`
U8 = u8,
// This variant requires `__Wit == &'a str`
Str = &'a str,
}
}
索引多态
此函数演示了 const fn 多态和通过实现 TypeFn
投影 TypeEq
。
(此示例需要 Rust 1.71.0,因为它在 const 环境中使用 <[T]>::split_at
。
use std::ops::Range;
use typewit::{HasTypeWitness, TypeEq};
fn main() {
let array = [3, 5, 8, 13, 21, 34, 55, 89];
assert_eq!(index(&array, 0), &3);
assert_eq!(index(&array, 3), &13);
assert_eq!(index(&array, 0..4), [3, 5, 8, 13]);
assert_eq!(index(&array, 3..5), [13, 21]);
}
const fn index<T, I>(slice: &[T], idx: I) -> &SliceIndexRet<I, T>
where
I: SliceIndex<T>,
{
// `I::WITNESS` is `<I as HasTypeWitness<IndexWitness<I>>>::WITNESS`,
match I::WITNESS {
IndexWitness::Usize(arg_te) => {
// `arg_te` (a `TypeEq<I, usize>`) allows coercing between `I` and `usize`,
// because `TypeEq` is a value-level proof that both types are the same.
let idx: usize = arg_te.to_right(idx);
// using the `TypeFn` impl for `FnSliceIndexRet<T>` to
// map `TypeEq<I, usize>`
// to `TypeEq<SliceIndexRet<I, T>, SliceIndexRet<usize, T>>`
arg_te.project::<FnSliceIndexRet<T>>()
// converts`TypeEq<SliceIndexRet<I, T>, T>`
// to `TypeEq<&SliceIndexRet<I, T>, &T>`
.in_ref()
.to_left(&slice[idx])
}
IndexWitness::Range(arg_te) => {
let range: Range<usize> = arg_te.to_right(idx);
let ret: &[T] = slice_range(slice, range);
arg_te.project::<FnSliceIndexRet<T>>().in_ref().to_left(ret)
}
}
}
// This macro declares a type witness enum
typewit::simple_type_witness! {
// Declares `enum IndexWitness<__Wit>`
// (the `__Wit` type parameter is implicitly added after all generics)
enum IndexWitness {
// This variant requires `__Wit == usize`
Usize = usize,
// This variant requires `__Wit == Range<usize>`
Range = Range<usize>,
}
}
/// Trait for all types that can be used as slice indices
///
/// The `HasTypeWitness` supertrait allows getting a `IndexWitness<Self>`
/// with its `WITNESS` associated constant.
trait SliceIndex<T>: HasTypeWitness<IndexWitness<Self>> + Sized {
type Returns: ?Sized;
}
impl<T> SliceIndex<T> for usize {
type Returns = T;
}
impl<T> SliceIndex<T> for Range<usize> {
type Returns = [T];
}
type SliceIndexRet<I, T> = <I as SliceIndex<T>>::Returns;
// Declares `struct FnSliceIndexRet<T>`
// a type-level function (TypeFn implementor) from `I` to `SliceIndexRet<I, T>`
typewit::type_fn! {
struct FnSliceIndexRet<T>;
impl<I: SliceIndex<T>> I => SliceIndexRet<I, T>
}
const fn slice_range<T>(slice: &[T], range: Range<usize>) -> &[T] {
let suffix = slice.split_at(range.start).1;
suffix.split_at(range.end - range.start).0
}
当传递错误的类型作为索引时,编译时错误与普通泛型函数相同
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
向下转换 const 泛型类型
此示例演示从具有 const 参数的类型到该类型具体实例的“向下转换”。
use typewit::{const_marker::Usize, TypeCmp, TypeEq};
assert_eq!(*mutate(&mut Arr([])), Arr([]));
assert_eq!(*mutate(&mut Arr([1])), Arr([1]));
assert_eq!(*mutate(&mut Arr([1, 2])), Arr([1, 2]));
assert_eq!(*mutate(&mut Arr([1, 2, 3])), Arr([1, 3, 6])); // this is different!
assert_eq!(*mutate(&mut Arr([1, 2, 3, 4])), Arr([1, 2, 3, 4]));
#[derive(Debug, PartialEq)]
struct Arr<const N: usize>([u8; N]);
fn mutate<const N: usize>(arr: &mut Arr<N>) -> &mut Arr<N> {
if let TypeCmp::Eq(te) = Usize::<N>.equals(Usize::<3>) {
let tem = te // `te` is a `TypeEq<Usize<N>, Usize<3>>`
.project::<GArr>() // returns `TypeEq<Arr<N>, Arr<3>>`
.in_mut(); // returns `TypeEq<&mut Arr<N>, &mut Arr<3>>`
// `tem.to_right(arr)` downcasts `arr` to `&mut Arr<3>`
tetra_sum(tem.to_right(arr));
}
arr
}
fn tetra_sum(arr: &mut Arr<3>) {
arr.0[1] += arr.0[0];
arr.0[2] += arr.0[1];
}
// Declares `struct GArr`
// a type-level function (TypeFn implementor) from `Usize<N>` to `Arr<N>`
typewit::type_fn!{
struct GArr;
impl<const N: usize> Usize<N> => Arr<N>
}
构建器
使用类型见证帮助编码类型级枚举,并在函数内匹配该类型级枚举。
类型级枚举用于跟踪构建器中字段的初始化。
此示例需要 Rust 1.65.0,因为它使用泛型关联类型。
use typewit::HasTypeWitness;
fn main() {
// all default fields
assert_eq!(
StructBuilder::new().build(),
Struct{foo: "default value".into(), bar: vec![3, 5, 8]},
);
// defaulted bar field
assert_eq!(
StructBuilder::new().foo("hello").build(),
Struct{foo: "hello".into(), bar: vec![3, 5, 8]},
);
// defaulted foo field
assert_eq!(
StructBuilder::new().bar([13, 21, 34]).build(),
Struct{foo: "default value".into(), bar: vec![13, 21, 34]},
);
// all initialized fields
assert_eq!(
StructBuilder::new().foo("world").bar([55, 89]).build(),
Struct{foo: "world".into(), bar: vec![55, 89]},
);
}
#[derive(Debug, PartialEq, Eq)]
struct Struct {
foo: String,
bar: Vec<u32>,
}
struct StructBuilder<FooInit: InitState, BarInit: InitState> {
// If `FooInit` is `Uninit`, then this field is a `()`
// If `FooInit` is `Init`, then this field is a `String`
foo: BuilderField<FooInit, String>,
// If `BarInit` is `Uninit`, then this field is a `()`
// If `BarInit` is `Init`, then this field is a `Vec<u32>`
bar: BuilderField<BarInit, Vec<u32>>,
}
impl StructBuilder<Uninit, Uninit> {
pub const fn new() -> Self {
Self {
foo: (),
bar: (),
}
}
}
impl<FooInit: InitState, BarInit: InitState> StructBuilder<FooInit, BarInit> {
/// Sets the `foo` field
pub fn foo(self, foo: impl Into<String>) -> StructBuilder<Init, BarInit> {
StructBuilder {
foo: foo.into(),
bar: self.bar,
}
}
/// Sets the `bar` field
pub fn bar(self, bar: impl Into<Vec<u32>>) -> StructBuilder<FooInit, Init> {
StructBuilder {
foo: self.foo,
bar: bar.into(),
}
}
/// Builds `Struct`,
/// providing default values for fields that haven't been set.
pub fn build(self) -> Struct {
Struct {
foo: init_or_else::<FooInit, _, _>(self.foo, || "default value".to_string()),
bar: init_or_else::<BarInit, _, _>(self.bar, || vec![3, 5, 8]),
}
}
}
// Emulates a type-level `enum InitState { Init, Uninit }`
trait InitState: Sized + HasTypeWitness<InitWit<Self>> {
// How a builder represents an initialized/uninitialized field.
// If `Self` is `Uninit`, then this is `()`.
// If `Self` is `Init`, then this is `T`.
type BuilderField<T>;
}
// If `I` is `Uninit`, then this evaluates to `()`
// If `I` is `Init`, then this evaluates to `T`
type BuilderField<I, T> = <I as InitState>::BuilderField::<T>;
/// Gets `T` out of `maybe_init` if it's actually initialized,
/// otherwise returns `else_()`.
fn init_or_else<I, T, F>(maybe_init: BuilderField<I, T>, else_: F) -> T
where
I: InitState,
F: FnOnce() -> T
{
typewit::type_fn! {
// Declares the `HelperFn` type-level function (TypeFn implementor)
// from `I` to `BuilderField<I, T>`
struct HelperFn<T>;
impl<I: InitState> I => BuilderField<I, T>
}
// matching on the type-level `InitState` enum by using `InitWit`.
// `WITNESS` comes from the `HasTypeWitness` trait
match I::WITNESS {
// `te: TypeEq<FooInit, Init>`
InitWit::InitW(te) => {
te.map(HelperFn::NEW) //: TypeEq<BuilderField<I, T>, T>
.to_right(maybe_init)
}
InitWit::UninitW(_) => else_(),
}
}
// Emulates a type-level `InitState::Init` variant.
// Marks a field as initialized.
enum Init {}
impl InitState for Init {
type BuilderField<T> = T;
}
// Emulates a type-level `InitState::Uninit` variant.
// Marks a field as uninitialized.
enum Uninit {}
impl InitState for Uninit {
type BuilderField<T> = ();
}
typewit::simple_type_witness! {
// Declares `enum InitWit<__Wit>`, a type witness.
// (the `__Wit` type parameter is implicitly added after all generics)
enum InitWit {
// This variant requires `__Wit == Init`
InitW = Init,
// This variant requires `__Wit == Uninit`
UninitW = Uninit,
}
}
Cargo 功能
以下是此包的功能。
默认功能
这些功能默认启用
"proc_macros"
:使用 proc 宏来改善涉及宏生成的 impl 的编译错误。
Rust 版本和标准包
这些功能启用了具有最低 Rust 版本的项目
-
"rust_stable"
:启用所有"rust_1_*"
功能。 -
"rust_1_65"
:启用type_constructors
模块、methods
模块以及"rust_1_61"
功能。 -
"rust_1_61"
:启用MetaBaseTypeWit
、BaseTypeWitness
以及{TypeCmp, TypeNe}::{zip*, in_array}
方法。
这些功能启用了需要非 core
标准包的项目
"alloc"
:启用使用标准alloc
包中的任何内容的项。
夜间功能
这些功能需要夜间 Rust 编译器
-
"adt_const_marker"
:启用"rust_stable"
包功能,并在const_marker
模块中的非原生const
参数的标记类型。 -
"nightly_mut_refs"
:启用"rust_stable"
和"mut_refs"
包功能,并将使用可变引用的函数转换为const fn
。
未来 Rust 功能
这些功能目前需要未来的编译器版本
"mut_refs"
:将接受可变引用的函数转换为const fn
。注意:截至 2023 年 10 月,此包功能需要未来的稳定编译器。
无标准支持
typewit
是 #![no_std]
,它可以在 Rust 可以使用的任何地方使用。
您需要启用 "alloc"
功能,以启用使用标准 alloc
crate 的任何项目。
支持的最低Rust版本
typewit
支持 Rust 1.57.0。
需要较新版本Rust或夜间编译器的功能,需要通过crate功能显式启用。