Type gymnastics of heterogeneously-typed lists

I'm designing a pass system inspired by frunk.

This is a little introduction of the Selector in frunk

Roughly speaking, frunk uses a Selector to simulate specialization in a very graceful way:

pub struct HCons<H, T> {
    pub head: H,
    pub tail: T,
}
pub trait Selector<S, I> {
    fn get(&self) -> &S;
    fn get_mut(&mut self) -> &mut S;
}
impl<T, Tail> Selector<T, Here> for HCons<T, Tail> {
    fn get(&self) -> &T {
        &self.head
    }
    fn get_mut(&mut self) -> &mut T {
        &mut self.head
    }
}

impl<Head, Tail, FromTail, TailIndex> Selector<FromTail, There<TailIndex>> for HCons<Head, Tail>
where
    Tail: Selector<FromTail, TailIndex>,
{
    fn get(&self) -> &FromTail {
        self.tail.get()
    }
    fn get_mut(&mut self) -> &mut FromTail {
        self.tail.get_mut()
    }
}

With the introduction of the second generic parameter, Rust compiler could infer type with some kind of specialization. Now we can use this like:

let h = hlist![1i32, 2u32, "hello", true, 42f32];

// Often, type inference can figure out the type you want.
// You can help guide type inference when necessary by
// using type annotations.
let b: &bool = h.get();
if !b { panic!("no way!") };

// If space is tight, you can also use turbofish syntax.
// The Index is still left to type inference by using `_`.
match *h.get::<u32, _>() {
    2 => { }
    _ => panic!("it can't be!!"),
}

I design a pass trait as

trait Pass {
    fn do_a<I>(pass_chain: &mut PC, ...)
    where
        PC: Selector<Self, I>;
    fn do_b<I>(pass_chain: &mut PC, ...)
    where
        PC: Selector<Self, I>;
    // ...
}

Where a pass chain would be something like a hlist containing different passes.

In the impl of each do_a, do_b, I want each pass can pass the execution towards other funcs in the same pass, or pass to next pass, or pass to the initial pass to redo the whole thing, which means I want each pass to have abilities to invoke do_a, do_b of current pass, next pass, and initial pass of the pass chain.

As a result, I modify the Selector trait like this:

pub trait Selector<S, I> {
    type Next;

    fn get_current(&self) -> &S;
    fn get_current_mut(&mut self) -> &mut S;

    fn get_next(&self) -> &Self::Next;
    fn get_next_mut(&mut self) -> &mut Self::Next;
}

impl<T, Tail> Selector<T, Here> for (T, Tail) {
    type Next = Tail;

    fn get_current(&self) -> &T {
        let (head, _) = self;
        head
    }

    fn get_current_mut(&mut self) -> &mut T {
        let (head, _) = self;
        head
    }

    fn get_next(&self) -> &Tail {
        let (_, next) = self;
        next
    }

    fn get_next_mut(&mut self) -> &mut Tail {
        let (_, next) = self;
        next
    }
}

impl<Head, Tail, FromTail, TailIndex> Selector<FromTail, There<TailIndex>> for (Head, Tail)
where
    Tail: Selector<FromTail, TailIndex>,
{
    type Next = Tail::Next;

    fn get_current(&self) -> &FromTail {
        let (_, tail) = self;
        tail.get_current()
    }

    fn get_current_mut(&mut self) -> &mut FromTail {
        let (_, tail) = self;
        tail.get_current_mut()
    }

    fn get_next(&self) -> &Tail::Next {
        let (_, tail) = self;
        tail.get_next()
    }

    fn get_next_mut(&mut self) -> &mut Tail::Next {
        let (_, tail) = self;
        tail.get_next_mut()
    }
}

However, I could not find out how to write a generic bound for the Next element and PC, so that I could write codes like

impl Pass for PassA {
    fn do_a<I>(pass_chain: &mut PC, ...)
    where
        PC: Selector<Self, I>
    {
         PC::Next::do_a(pass_chain, ...); // Let next passes do other things
         PC::do_a(pass_chain, ...); // Redo the whole thing from the beginning of the chain
    }
}

This problem has made me struggled for about two weeks. Any advice will be appreciated!

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.