Type gymnastics of heterogeneously-typed lists

⚓ Rust    📅 2025-08-03    👤 surdeus    👁️ 11      

surdeus

Warning

This post was published 122 days ago. The information described in this article may have changed.

I'm designing a pass system inspired by frunk.

This is a little introduction of the Selector in frunk (click for more details)

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!

1 post - 1 participant

Read full topic

🏷️ Rust_feed