Nested Generics Inversion Magic Using GAT (or how to write a typed builder for typed linked list)
Just discovered some really wierd pattern using GAT's type solver.
If you have a nested generic type B3<B2<B1<()>>>, and you associate each Bx with a generic type Cx, you can actually let the GAT type solver create a inverted C1<C2<C3<()>>> for you.
struct B1<BPrev> {
previous: BPrev,
}
struct B2<BPrev> {
previous: BPrev,
}
struct B3<BPrev> {
previous: BPrev,
}
trait Builder {
type R<RNext>;
fn build<RNext>(&self, next: RNext) -> Self::R<RNext>;
}
impl Builder for () {
type R<RNext> = RNext;
fn build<RNext>(&self, next: RNext) -> Self::R<RNext> {
next
}
}
impl<BPrev: Builder> Builder for B1<BPrev> {
type R<RNext> = BPrev::R<R1<RNext>>;
fn build<RNext>(&self, next: RNext) -> Self::R<RNext> {
self.previous.build(R1 { next })
}
}
impl<BPrev: Builder> Builder for B2<BPrev> {
type R<RNext> = BPrev::R<R2<RNext>>;
fn build<RNext>(&self, next: RNext) -> Self::R<RNext> {
self.previous.build(R2 { next })
}
}
impl<BPrev: Builder> Builder for B3<BPrev> {
type R<RNext> = BPrev::R<R3<RNext>>;
fn build<RNext>(&self, next: RNext) -> Self::R<RNext> {
self.previous.build(R3 { next })
}
}
struct R1<RNext> {
next: RNext,
}
struct R2<RNext> {
next: RNext,
}
struct R3<RNext> {
next: RNext,
}
#[test]
fn test() {
let b = ();
let b = B1 { previous: b }; // B1<()>
let b = B2 { previous: b }; // B2<B1<()>>
let b = B3 { previous: b }; // B3<B2<B1<()>>>
let r = b.build(()); // R1<R2<R3<()>>>
fn type_of<T: 'static>(x: T) -> TypeId {
TypeId::of::<T>()
}
assert_eq!(type_of(b), TypeId::of::<B3<B2<B1<()>>>>());
assert_eq!(type_of(r), TypeId::of::<R1<R2<R3<()>>>>());
}
I know GAT can do a lot of wierd stuff, but this thing...... Does it have a name or something?
One usage I think of is to create a typed builder for a typed linked list that has forward type order during building, Somthing like:
let builder = TypeLinkedListBuilder::new()
.add::<T1>(t1)
.add::<T2>(t2)
.add::<T3>(t3); // Builder<T3, Builder<T2, Builder<T1, Builder<()>>>>
let list = builder.build(); // Node<T1, Node<T2, Node<T3, ()>>>>
let (t1, list) = list.pop(); // T1
let (t2, list) = list.pop(); // T2
let (t3, ()) = list.pop(); // T3