diff --git a/README.md b/README.md index 6b198775f..a1086312d 100644 --- a/README.md +++ b/README.md @@ -34,14 +34,87 @@ type Y = Exp; assert_eq!(::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; +} +``` +
+ + Unfold here to see a (somwhat contrived) exploitation of type-level integer arithmetic: + + ```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, 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; + } + + + // 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 Flatten for FlattenDemo + 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, + // 2. We also must specify that this `Mul`tiply behaviors `Output` implements the + // "`Add`ition on `U7`" behavior: + op!(Width * Height): Add, + // 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: + <>::Output as Add>::Output: Div, + // With the convenience macro: + // op!((Width * Height) + U7): Div, + 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 { + todo!() + } + } + ``` +
+ +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