Macro to impl trait for tuple

I want to implement a trait for tuples, so I'm using a macro.
For tuples I want to apply my trait operator element-wisely.
i.e
(self.0.foo(other.0), self.1.foo(other.1), self.2.foo(other.2))

But I don't get how to actually implement this with macro.

trait Foo {
    fn foo(self, other: Self) -> Self;
}

macro_rules! impl_tuple {
    ($($t:tt)+) => {
        impl<$($t,)+> Foo for ($($t,)+)
        where
            $($t: Foo,)+
        {
            fn foo(self, other: Self) -> Self {
                let ($($t,)+) = self;
                let ($($t,)+) = other;
                // ???
            }
        }
    };
}

impl_tuple!(A B C D E F G H I J);
impl_tuple!(A B C D E F G H I);
impl_tuple!(A B C D E F G H);
impl_tuple!(A B C D E F G);
impl_tuple!(A B C D E F);
impl_tuple!(A B C D E);
impl_tuple!(A B C D);
impl_tuple!(A B C);
impl_tuple!(A B);
impl_tuple!(A);
macro_rules! impl_tuple {
    ($($idx:tt $t:tt),+) => {
        impl<$($t,)+> Foo for ($($t,)+)
        where
            $($t: Foo,)+
        {
            fn foo(self, other: Self) -> Self {
                ($(
                    $t :: foo(self.$idx, other.$idx),
                )+)
            }
        }
    };
}

impl_tuple!(0 A, 1 B, 2 C, 3 D, 4 E);
impl_tuple!(0 A, 1 B, 2 C, 3 D);
impl_tuple!(0 A, 1 B, 2 C);
impl_tuple!(0 A, 1 B);
impl_tuple!(0 A);

with expanded output:

impl<A, B, C, D, E> Foo for (A, B, C, D, E)
where
    A: Foo,
    B: Foo,
    C: Foo,
    D: Foo,
    E: Foo,
{
    fn foo(self, other: Self) -> Self {
        (
            A::foo(self.0, other.0),
            B::foo(self.1, other.1),
            C::foo(self.2, other.2),
            D::foo(self.3, other.3),
            E::foo(self.4, other.4),
        )
    }
}
impl<A, B, C, D> Foo for (A, B, C, D)
where
    A: Foo,
    B: Foo,
    C: Foo,
    D: Foo,
{
    fn foo(self, other: Self) -> Self {
        (
            A::foo(self.0, other.0),
            B::foo(self.1, other.1),
            C::foo(self.2, other.2),
            D::foo(self.3, other.3),
        )
    }
}
impl<A, B, C> Foo for (A, B, C)
where
    A: Foo,
    B: Foo,
    C: Foo,
{
    fn foo(self, other: Self) -> Self {
        (
            A::foo(self.0, other.0),
            B::foo(self.1, other.1),
            C::foo(self.2, other.2),
        )
    }
}
impl<A, B> Foo for (A, B)
where
    A: Foo,
    B: Foo,
{
    fn foo(self, other: Self) -> Self {
        (A::foo(self.0, other.0), B::foo(self.1, other.1))
    }
}
impl<A> Foo for (A,)
where
    A: Foo,
{
    fn foo(self, other: Self) -> Self {
        (A::foo(self.0, other.0),)
    }
}
1 Like

Since you need to have two local variables per tuple entry, using paste - Rust will be useful for generating distinct (and also properly cased) variable names.

trait Foo {
    fn foo(self, other: Self) -> Self;
}

use paste::paste;

macro_rules! impl_tuple {
    ($($T:tt)*) => {
        paste! {
            impl<$($T,)*> Foo for ($($T,)*)
            where
                $($T: Foo,)*
            {
                fn foo(self, other: Self) -> Self {
                    let ($([<$T:lower 1>],)*) = self;
                    let ($([<$T:lower 2>],)*) = other;
                    ($([<$T:lower 1>].foo([<$T:lower 2>]),)*)
                }
            }
        }
    };
}

impl_tuple!(A B C D E F G H I J);
impl_tuple!(A B C D E F G H I);
impl_tuple!(A B C D E F G H);
impl_tuple!(A B C D E F G);
impl_tuple!(A B C D E F);
impl_tuple!(A B C D E);
impl_tuple!(A B C D);
impl_tuple!(A B C);
impl_tuple!(A B);
impl_tuple!(A);
impl_tuple!(); // empty tuple impl, for good measure

part of cargo expand output:

(note that the formatting step of cargo expand has apparently removed some trailing commas, so this is not exactly the macro output)

impl<A, B, C, D> Foo for (A, B, C, D) where A: Foo, B: Foo, C: Foo, D: Foo {
    fn foo(self, other: Self) -> Self {
        let (a1, b1, c1, d1) = self;
        let (a2, b2, c2, d2) = other;
        (a1.foo(a2), b1.foo(b2), c1.foo(c2), d1.foo(d2))
    }
}
impl<A, B, C> Foo for (A, B, C) where A: Foo, B: Foo, C: Foo {
    fn foo(self, other: Self) -> Self {
        let (a1, b1, c1) = self;
        let (a2, b2, c2) = other;
        (a1.foo(a2), b1.foo(b2), c1.foo(c2))
    }
}
impl<A, B> Foo for (A, B) where A: Foo, B: Foo {
    fn foo(self, other: Self) -> Self {
        let (a1, b1) = self;
        let (a2, b2) = other;
        (a1.foo(a2), b1.foo(b2))
    }
}
impl<A> Foo for (A,) where A: Foo {
    fn foo(self, other: Self) -> Self {
        let (a1,) = self;
        let (a2,) = other;
        (a1.foo(a2),)
    }
}
impl Foo for () where  {
    fn foo(self, other: Self) -> Self { let () = self; let () = other; () }
}

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.