6

Suppose that I have a

trait Happy {}

I can implement Happy for whatever struct I might want, for example:

struct Dog;
struct Cat;
struct Alligator;

impl Happy for Dog {}
impl Happy for Cat {}
impl Happy for Alligator {}

Now, I would like to automatically impl my Happy trait for whatever tuple is made up of types that all implement the Happy trait. Intuitively, a tuple of all happy is happy as well.

Is it possible to do such a thing? For example, I can trivially extend the implementation of Happy to whatever tuple of two Happy types:

impl <T, Q> Happy for (T, Q) where T: Happy, Q: Happy {}

As a result, this compiles perfectly:

fn f(_: impl Happy) {
}

fn main() {
    f((Dog{}, Alligator{}));
}

But how could I generalize that to any tuple, of any length? As far as my understanding goes, we don't have variadic generics in Rust. Is there a workaround?

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Matteo Monti
  • 8,362
  • 19
  • 68
  • 114
  • 1
    Macros. That's how it is done currently. – Boiethios Apr 06 '19 at 20:48
  • 1
    I'm not sure if it's possible. Even the standard library has to [provide implementations for every possible length of a tuple, up to 12](https://doc.rust-lang.org/std/primitive.tuple.html#trait-implementations). – ForceBru Apr 06 '19 at 20:49

1 Answers1

10

we don't have variadic generics in Rust.

Correct.

Is there a workaround?

You use a macro:

trait Happy {}

macro_rules! tuple_impls {
    ( $head:ident, $( $tail:ident, )* ) => {
        impl<$head, $( $tail ),*> Happy for ($head, $( $tail ),*)
        where
            $head: Happy,
            $( $tail: Happy ),*
        {
            // interesting delegation here, as needed
        }

        tuple_impls!($( $tail, )*);
    };

    () => {};
}

tuple_impls!(A, B, C, D, E, F, G, H, I, J,);

This now compiles:

fn example<T: Happy>() {}

fn call<A: Happy, B: Happy>() {
    example::<(A, B)>();
} 

This isn't generally seen as a big problem because long tuples are basically unreadable and you can always nest tuples if really needed.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Just want to point out that while it is custom to have a trait implemented for `(A, ) + (A, B) + (A, B, C)`, this answer will implement it for `(A, B, C) + (B, C) + (C, )` etc. i.e. it recursively pops the first letter rather than the last. Semantically, this is identical to the usual behavior, but it does make a difference in `rustdoc`, vscode type hints and/or other places depending on your setup. – cutsoy Sep 24 '22 at 20:40