Skip to content

Commit 1c2c2d2

Browse files
Merge pull request #9 from rodrimati1992/1_7_0
1.7.0
2 parents 9a0664e + 9f66ec8 commit 1c2c2d2

File tree

15 files changed

+832
-29
lines changed

15 files changed

+832
-29
lines changed

.github/workflows/rust.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,18 @@ jobs:
1717
strategy:
1818
max-parallel: 2
1919
matrix:
20-
rust: [stable, beta, nightly, 1.61.0, 1.57.0]
20+
rust: [stable, beta, nightly, 1.65.0, 1.61.0, 1.57.0]
2121

2222
steps:
2323
- uses: actions/checkout@v2
2424
- name: enable-rust-1_61
2525
if: matrix.rust == '1.61.0'
2626
run: echo "rustv=rust_1_61" >> $GITHUB_ENV
2727

28+
- name: enable-rust-1_65
29+
if: matrix.rust == '1.65.0'
30+
run: echo "rustv=rust_1_65" >> $GITHUB_ENV
31+
2832
- name: enable-rust-stable
2933
if: matrix.rust == 'stable' || matrix.rust == 'beta' || matrix.rust == 'nightly'
3034
run: echo "rustv=rust_stable" >> $GITHUB_ENV

Cargo.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "typewit"
3-
version = "1.6.0"
3+
version = "1.7.0"
44
authors = ["rodrimati1992 <rodrimatt1985@gmail.com>"]
55
rust-version = "1.57.0"
66
edition = "2021"
@@ -28,7 +28,9 @@ version = "1.0"
2828
[features]
2929
default = ["const_marker"]
3030
rust_1_61 = []
31-
rust_stable = ["rust_1_61"]
31+
# mostly for doc examples
32+
rust_1_65 = ["rust_1_61"]
33+
rust_stable = ["rust_1_65"]
3234
const_marker = []
3335
adt_const_marker = ["rust_stable","const_marker"]
3436
alloc = []

Changelog.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,10 @@ This is the changelog, summarising changes in each version(some minor changes ma
22

33
# 1.0
44

5+
### 1.7.0
6+
7+
Added `polymatch` macro.
8+
59
### 1.6.0
610

711
Added `"adt_const_marker"` feature.

README.md

Lines changed: 159 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ This crate provides abstractions for creating
77
[type witnesses](#what-are-type-witnesses).
88

99
The inciting motivation for this crate is emulating trait polymorphism in `const fn`
10-
(as of 2023-07-31, it's not possible to call trait methods in const contexts on stable).
10+
(as of 2023-09-10, it's not possible to call trait methods in const contexts on stable).
1111

1212
# What are type witnesses
1313

@@ -27,7 +27,7 @@ which can coerce between a type parameter and as many types as there are variant
2727
### Polymorphic function
2828

2929
This demonstrates how one can write a return-type-polymorphic `const fn`
30-
(as of 2023-07-31, trait methods can't be called in const fns)
30+
(as of 2023-09-10, trait methods can't be called in const fns on stable)
3131

3232
```rust
3333
use typewit::{MakeTypeWitness, TypeEq};
@@ -75,7 +75,7 @@ typewit::simple_type_witness! {
7575
This function demonstrates const fn polymorphism
7676
and projecting [`TypeEq`] by implementing [`TypeFn`].
7777

78-
(this example requires Rust 1.61.0, because of the `I: SliceIndex<T>,` bound)
78+
(this example requires Rust 1.71.0, because it uses `<[T]>::split_at` in a const context.
7979
```rust
8080
use std::ops::Range;
8181

@@ -154,6 +154,12 @@ typewit::type_fn! {
154154

155155
impl<I: SliceIndex<T>> I => SliceIndexRet<I, T>
156156
}
157+
158+
const fn slice_range<T>(slice: &[T], range: Range<usize>) -> &[T] {
159+
let suffix = slice.split_at(range.start).1;
160+
suffix.split_at(range.end - range.start).0
161+
}
162+
157163
```
158164

159165
When the wrong type is passed for the index,
@@ -217,6 +223,156 @@ typewit::type_fn!{
217223
impl<const N: usize> Usize<N> => Arr<N>
218224
}
219225
```
226+
### Builder
227+
228+
Using a type witness to help encode a type-level enum,
229+
and to match on that type-level enum inside of a function.
230+
231+
The type-level enum is used to track the initialization of fields in a builder.
232+
233+
This example requires Rust 1.65.0, because it uses Generic Associated Types.
234+
```rust
235+
use typewit::HasTypeWitness;
236+
237+
fn main() {
238+
// all default fields
239+
assert_eq!(
240+
StructBuilder::new().build(),
241+
Struct{foo: "default value".into(), bar: vec![3, 5, 8]},
242+
);
243+
244+
// defaulted bar field
245+
assert_eq!(
246+
StructBuilder::new().foo("hello").build(),
247+
Struct{foo: "hello".into(), bar: vec![3, 5, 8]},
248+
);
249+
250+
// defaulted foo field
251+
assert_eq!(
252+
StructBuilder::new().bar([13, 21, 34]).build(),
253+
Struct{foo: "default value".into(), bar: vec![13, 21, 34]},
254+
);
255+
256+
// all initialized fields
257+
assert_eq!(
258+
StructBuilder::new().foo("world").bar([55, 89]).build(),
259+
Struct{foo: "world".into(), bar: vec![55, 89]},
260+
);
261+
}
262+
263+
264+
#[derive(Debug, PartialEq, Eq)]
265+
struct Struct {
266+
foo: String,
267+
bar: Vec<u32>,
268+
}
269+
270+
struct StructBuilder<FooInit: InitState, BarInit: InitState> {
271+
// If `FooInit` is `Uninit`, then this field is a `()`
272+
// If `FooInit` is `Init`, then this field is a `String`
273+
foo: BuilderField<FooInit, String>,
274+
275+
// If `BarInit` is `Uninit`, then this field is a `()`
276+
// If `BarInit` is `Init`, then this field is a `Vec<u32>`
277+
bar: BuilderField<BarInit, Vec<u32>>,
278+
}
279+
280+
impl StructBuilder<Uninit, Uninit> {
281+
pub const fn new() -> Self {
282+
Self {
283+
foo: (),
284+
bar: (),
285+
}
286+
}
287+
}
288+
289+
impl<FooInit: InitState, BarInit: InitState> StructBuilder<FooInit, BarInit> {
290+
/// Sets the `foo` field
291+
pub fn foo(self, foo: impl Into<String>) -> StructBuilder<Init, BarInit> {
292+
StructBuilder {
293+
foo: foo.into(),
294+
bar: self.bar,
295+
}
296+
}
297+
298+
/// Sets the `bar` field
299+
pub fn bar(self, bar: impl Into<Vec<u32>>) -> StructBuilder<FooInit, Init> {
300+
StructBuilder {
301+
foo: self.foo,
302+
bar: bar.into(),
303+
}
304+
}
305+
306+
/// Builds `Struct`,
307+
/// providing default values for fields that haven't been set.
308+
pub fn build(self) -> Struct {
309+
typewit::type_fn! {
310+
struct HelperFn<T>;
311+
impl<I: InitState> I => BuilderField<I, T>
312+
}
313+
314+
Struct {
315+
// matching on the type-level `InitState` enum by using `InitWit`.
316+
// `WITNESS` comes from the `HasTypeWitness` trait
317+
foo: match FooInit::WITNESS {
318+
// `te: TypeEq<FooInit, Init>`
319+
InitWit::InitW(te) => {
320+
te.map(HelperFn::NEW) //: TypeEq<BuilderField<FooInit, String>, String>
321+
.to_right(self.foo)
322+
}
323+
InitWit::UninitW(_) => "default value".to_string(),
324+
},
325+
bar: match BarInit::WITNESS {
326+
// `te: TypeEq<BarInit, Init>`
327+
InitWit::InitW(te) => {
328+
te.map(HelperFn::NEW) //: TypeEq<BuilderField<BarInit, Vec<u32>>, Vec<u32>>
329+
.to_right(self.bar)
330+
}
331+
InitWit::UninitW(_) => vec![3, 5, 8],
332+
},
333+
}
334+
}
335+
}
336+
337+
// Emulates a type-level `enum InitState { Init, Uninit }`
338+
trait InitState: Sized + HasTypeWitness<InitWit<Self>> {
339+
// How a builder represents an initialized/uninitialized field.
340+
// If `Self` is `Uninit`, then this is `()`.
341+
// If `Self` is `Init`, then this is `T`.
342+
type BuilderField<T>;
343+
}
344+
345+
// If `I` is `Uninit`, then this evaluates to `()`
346+
// If `I` is `Init`, then this evaluates to `T`
347+
type BuilderField<I, T> = <I as InitState>::BuilderField::<T>;
348+
349+
// Emulates a type-level `InitState::Init` variant.
350+
// Marks a field as initialized.
351+
enum Init {}
352+
353+
impl InitState for Init {
354+
type BuilderField<T> = T;
355+
}
356+
357+
// Emulates a type-level `InitState::Uninit` variant
358+
// Marks a field as uninitialized.
359+
enum Uninit {}
360+
361+
impl InitState for Uninit {
362+
type BuilderField<T> = ();
363+
}
364+
365+
typewit::simple_type_witness! {
366+
// Declares `enum InitWit<__Wit>`, a type witness.
367+
// (the `__Wit` type parameter is implicitly added after all generics)
368+
enum InitWit {
369+
// This variant requires `__Wit == Init`
370+
InitW = Init,
371+
// This variant requires `__Wit == Uninit`
372+
UninitW = Uninit,
373+
}
374+
}
375+
```
220376

221377

222378
# Cargo features

src/const_marker/const_witnesses.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ use crate::{
3131
/// impl<const B: bool> Bool<B> => Incrementor<B>
3232
/// }
3333
///
34+
/// // The example below this one shows how to write this match more concisely
3435
/// match BoolWit::MAKE {
3536
/// // `bw: TypeEq<Bool<B>, Bool<true>>`
3637
/// BoolWit::True(bw) => {
@@ -76,6 +77,49 @@ use crate::{
7677
///
7778
/// ```
7879
///
80+
/// ### Using `polymatch` for conciseness
81+
///
82+
/// The [`polymatch`](crate::polymatch) macro can be used to
83+
/// more concisely implement the `call_next` function.
84+
///
85+
/// ```
86+
/// # use typewit::{const_marker::{Bool, BoolWit}, MakeTypeWitness};
87+
/// #
88+
/// const fn call_next<const B: bool>(incrementor: Incrementor<B>) -> Incrementor<B> {
89+
/// typewit::type_fn! {
90+
/// struct IncrementorFn;
91+
/// impl<const B: bool> Bool<B> => Incrementor<B>
92+
/// }
93+
///
94+
/// // expands to a match with two arms,
95+
/// // one for `BoolWit::True` and one for `BoolWit::False`,
96+
/// // copying the expression to the right of the `=>` to both arms.
97+
/// typewit::polymatch! {BoolWit::MAKE;
98+
/// BoolWit::True(bw) | BoolWit::False(bw) => {
99+
/// let te = bw.project::<IncrementorFn>();
100+
/// te.to_left(te.to_right(incrementor).next())
101+
/// }
102+
/// }
103+
/// }
104+
/// #
105+
/// # #[derive(Debug, Copy, Clone, PartialEq, Eq)]
106+
/// # struct Incrementor<const GO_UP: bool>(usize);
107+
/// #
108+
/// # const GO_UP: bool = true;
109+
/// # const GO_DOWN: bool = false;
110+
/// #
111+
/// # impl Incrementor<GO_DOWN> {
112+
/// # #[track_caller]
113+
/// # pub const fn next(self) -> Self { unimplemented!() }
114+
/// # }
115+
/// #
116+
/// # impl Incrementor<GO_UP> {
117+
/// # pub const fn next(self) -> Self { unimplemented!() }
118+
/// # }
119+
/// ```
120+
///
121+
/// ### What happens without `BoolWit`
122+
///
79123
/// If the `call_next` function was defined like this:
80124
/// ```rust,compile_fail
81125
/// # use typewit::{const_marker::{Bool, BoolWit}, MakeTypeWitness};

0 commit comments

Comments
 (0)