Skip to content

Commit

Permalink
Add example
Browse files Browse the repository at this point in the history
  • Loading branch information
Ben-PH committed May 17, 2024
1 parent b237efb commit 3ac8fd7
Showing 1 changed file with 76 additions and 3 deletions.
79 changes: 76 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,14 +34,87 @@ type Y = Exp<N2, P3>;
assert_eq!(<Y as Integer>::to_i32(), -8);
```

For a non-trivial example of its use, see one of the crates that depends on
it. The full list is
[here](https://crates.io/crates/typenum/reverse_dependencies). Of note are

And how to use constraints:

```rust
use typenum::{self, IsGreater, True, U0};
trait NonZero {
type NonZero: IsGreater<U0, Output = True>;
}
```
<details>

<summary>Unfold here to see a (somwhat contrived) exploitation of type-level integer arithmetic:</summary>

```rust
// Demo-time. A simple "flatten a 2d bool-array to a 1d byte-array"
// Let's encapsulate a 2d array, similar to `[[bool; WIDTH]; HEIGHT]`
pub struct FlattenDemo<
Width: ArrayLength,
Height: ArrayLength,
> {
unflattened: GenericArray<GenericArray<bool, Width >, Height>,
}

// For fun and profit, let's wrap-up the behavior in a trait.
trait Flatten {
// NOTE: the `generic-array` crate is pretty cool.
type FlattenedLen: ArrayLength;
fn flattened(self) -> GenericArray<bool, Self::FlattenedLen>;
}


// So here is the fun part: Flattening using compile-time maths, but using the type-system.
// Flattening into a byte-array, you must ensure a multiple of 8, to Illustrate:
// A: 2 x 3 = 6. so need a 1-byte array
// B: 2 x 4 = 8: 8 is the nearest round-up, again; 1-byte array
// C: 3 x 7 = 21: need to round up to 24: 3-byte array
//
// This is done by taking the needed bits, adding 7, then integer-division to 8
// A: (6 + 7) / 8 = 13 / 8 = 1
// B: (8 + 7) / 8 = 15 / 8 = 1
// A: (21 + 7) / 8 = 28 / 8 = 3
impl<Width: ArrayLength, Height: ArrayLength> Flatten for FlattenDemo<Width, Height>
where
// Types are not values: Unlike integer values, all of which implement the behavior of
// integers (multiplication, addition, division, etc) as an inherent part of the language,
// the compiler has no way of knowing if a given type implements a given operation unless
// you explicitly specify...
//
// 1. We must specify that the `Width` type must implement behavior in which it `Mul`tiplies
// the `Height` type:
Width: Mul<Height>,
// 2. We also must specify that this `Mul`tiply behaviors `Output` implements the
// "`Add`ition on `U7`" behavior:
op!(Width * Height): Add<U7>,
// 3. And so on: This is the constraint "The result of (width * height) + 7 must implement
// division by 8", but without the `op!` convenience macro:
<<Width as Mul<Height>>::Output as Add<U7>>::Output: Div<U8>,
// With the convenience macro:
// op!((Width * Height) + U7): Div<U8>,
op!((Width * Height + U7) / U8): ArrayLength,

{
// Et, voila! Through the power of the type system, we have lifted arithmetic to compile
// time, without the use of nightly, the need to think about machine-representation of
// values (usize/u64/etc).
type FlattenedLen = op!((Width * Height + U7) / U8);
fn flattened(self) -> GenericArray<bool, Self::FlattenedLen> {
todo!()
}
}
```
</details>

For more non-trivial examples, with real-world use, check the full list of
reverse dependencies [here](https://crates.io/crates/typenum/reverse_dependencies). Of note are
[dimensioned](https://crates.io/crates/dimensioned/) which does compile-time
type checking for arbitrary unit systems and
[generic-array](https://crates.io/crates/generic-array/) which provides arrays
whose length you can generically refer to.


### Error messages


Expand Down

0 comments on commit 3ac8fd7

Please sign in to comment.