Macros for "variadic" tuples

I have a few macros that allows for "variadic" tuples. You pass a macro, bundle, and a backwards array slice of generics to the recurse_and_apply!() macro or at least in this specific case. That macro then recurses and applys the bundle trait to the generics in the order of (A, B, C), (B, C), (C), and (). In theory it should work however it doesn't compile with the reason being conflicting implementations. I get these errors in this order.

conflicting implementations of trait `Bundle` for type `()`
conflicting implementation for `()`
conflicting implementations of trait `Bundle` for type `(_, _)`
conflicting implementation for `(_, _)`
conflicting implementations of trait `Bundle` for type `(_, _, _)`
conflicting implementation for `(_, _, _)`
conflicting implementations of trait `bundle::Bundle` for type `()`
conflicting implementation for `()`
conflicting implementations of trait `bundle::Bundle` for type `(_, _)`
conflicting implementation for `(_, _)`
conflicting implementations of trait `bundle::Bundle` for type `(_, _, _)`
conflicting implementation for `(_, _, _)`

And heres the code

pub trait Bundle {}

macro_rules! bundle {
    ( $( $item:ident ),* ) => {
        impl<$( $item ),*> Bundle for ($( $item ),*) {}
    };
}

macro_rules! recurse_and_apply {
    ( $macro:ident, [$head: tt] ) => {
        $macro!{$head}
        $macro!{}
    };
    ( $macro:ident, [$head: tt, $( $tail:tt ),*] ) => {
        recurse_and_apply!{$macro, [$( $tail ),*]}
        $macro!{$head, $( $tail ),*}
    };
}

recurse_and_apply!(bundle, [A, B, C]);

There is a temporary solution I have found with that being removing this line, $macro!{$head} from the recurse_and_apply!() macro. I have no idea why that works but it does. So my question to you the reader is how would I go about solving this issue. And please don't respond by saying to just use vectors instead I understand I can do that.

Here a solution that compiles:

pub trait Bundle {}

macro_rules! bundle {
    ( $( $item:ident ),+ ) => {
        impl<$( $item ),+> Bundle for ($( $item ),+,) {}
    };
    () => {
        impl Bundle for () {}
    };
}

macro_rules! recurse_and_apply {
    ( $macro:ident, [$head: tt] ) => {
        $macro!{$head}
        $macro!{}
    };
    ( $macro:ident, [$head: tt, $( $tail:tt ),+] ) => {
        recurse_and_apply!{$macro, [$( $tail ),+]}
        $macro!{$head, $( $tail ),*}
    };
}

recurse_and_apply!(bundle, [A, B, C]);

Playground.

The weird warning about the implementing type being wrapped unnecessarily in parenthesis gave me the idea to try and add a trailing comma at the end of the tuple. I.e. turn

impl<C> Bundle for (C) {}

into

impl<C> Bundle for (C,) {}

which is necessary to distinguish a tuple with a single element from a generic type C that is unnecessarily wrapped in parenthesis.

2 Likes

change to impl<$( $item ),*> Bundle for ($( $item, )*) {} or else it expands to (C) which I think means all types. A tuple type is notated (C,).

2 Likes

I figured this out right as both of you replied. Thank you!

the reverse and apply function keeps giving me an error and again it looks right

pub trait Bundle {}

pub trait Component {}

macro_rules! bundle {
    ( $trait:ident, [$( $item:ident ),*] ) => {
        impl<$( $item: $trait ),*> Bundle for ($( $item, )*) {}
    };
}

macro_rules! reverse_and_apply {
    ( $macro:ident, $trait:ident, [], [$( $item:ident ),*] ) => {
        $macro!{[$( $trait ),*], [$( $item ),*]}
    };
    ( $macro:ident, $trait:ident, [$head:ident, $( $tail:ident ),*], [$( $item:ident ),*] ) => {
        reverse_and_apply!{$macro, $trait, [$( $tail ),*], [$head, $( $item ),*]}
    };
}

macro_rules! recurse_and_apply {
    ( $macro:ident, $trait:ident, [$head:ident] ) => {
        $macro!{$trait, [$head]}
        $macro!{$trait, []}
    };
    ( $macro:ident, $trait:ident, [$head:ident, $( $tail:ident ),*] ) => {
        recurse_and_apply!{$macro, $trait, [$( $tail ),*]}
        reverse_and_apply!{$macro, $trait, [$head, $( $tail ),*], []}
    };
}

recurse_and_apply!(bundle, Component, [C, B, A]);

solved it same issue as before needed trailing comma

macro_rules! reverse_and_apply {
    ( $macro:ident, $trait:ident, [], [$( $item:ident, )*] ) => {
        $macro!{$trait, [$( $item ),*]}
    };
    ( $macro:ident, $trait:ident, [$head:ident, $( $tail:ident, )*], [$( $item:ident, )*] ) => {
        reverse_and_apply!{$macro, $trait, [$( $tail, )*], [$head, $( $item, )*]}
    };
}