6

I have a trait like this:

trait Foo {
    fn do_something(self) -> Self;
}

And I want implement this trait for a tuple that may have any number of elements that implement this trait.

impl<T> Foo for (T, T) where T: Foo {
    fn do_something(self) -> Self {
        let (t0, t1) = self;
        (t0.do_something(), t1.do_something())
    }
}

impl<T> Foo for (T, T, T) where T: Foo {
    fn do_something(self) -> Self {
        let (t0, t1, t2) = self;
        (t0.do_something(), t1.do_something(), t2.do_something())
    }
}

// and so on...
pretzelhammer
  • 13,874
  • 15
  • 47
  • 98
Yechao
  • 61
  • 1
  • 4
  • 1
    If all the items in the tuple are the same type, have you considered using an array or Vec instead? – loganfsmyth Jun 21 '19 at 05:51
  • Seems it's not possible for now. See how `Tuple` itself implements different traits: https://doc.rust-lang.org/1.5.0/std/primitive.tuple.html – Zefick Jun 21 '19 at 08:31

2 Answers2

6

Note: I'm not sure whether you should do this, but here's a way to anyway. (Feels hacky to me, and that may be because I don't know how to make a better macro.)

Homogeneous tuples (T, T)

The way you've described it:

impl<T> Foo for (T, T) where T: Foo

Here, the entire tuple must be homogeneous (i.e. (MyType, MyType2).do_something() will not work because of monomorphization). This raises a flag because tuples are for heterogeneous data.

If implementing the trait only one homogeneous tuples is still what you want, we can implement a macro the way the standard library does to implement traits for varied length tuples, with some modifications. (Click src on the right of an impl to see it's source.)

macro_rules! replace_expr {
    ($_t:tt $sub:ty) => {$sub};
}

macro_rules! tuple_impls {
    ( $( $name:ident )+ ) => {
        impl<T: Foo> Foo for ($(replace_expr!(($name) T),)+)
        {
            fn do_something(self) -> Self {
                let ($($name,)+) = self;
                ($($name.do_something(),)+)
            }
        }
    };
}

tuple_impls! { A }
tuple_impls! { A B }
tuple_impls! { A B C }
tuple_impls! { A B C D }
tuple_impls! { A B C D E }
tuple_impls! { A B C D E F }
tuple_impls! { A B C D E F G }
tuple_impls! { A B C D E F G H }
tuple_impls! { A B C D E F G H I }
tuple_impls! { A B C D E F G H I J }
tuple_impls! { A B C D E F G H I J K }
tuple_impls! { A B C D E F G H I J K L }

Playground

Heterogeneous tuples (T1, T2)

If you're okay with (MyType, MyType2).do_something() working (where both implement the Foo trait), you can try this simpler macro:

macro_rules! tuple_impls {
    ( $( $name:ident )+ ) => {
        impl<$($name: Foo),+> Foo for ($($name,)+)
        {
            fn do_something(self) -> Self {
                let ($($name,)+) = self;
                ($($name.do_something(),)+)
            }
        }
    };
}

tuple_impls! { A }
tuple_impls! { A B }
tuple_impls! { A B C }
tuple_impls! { A B C D }
tuple_impls! { A B C D E }
tuple_impls! { A B C D E F }
tuple_impls! { A B C D E F G }
tuple_impls! { A B C D E F G H }
tuple_impls! { A B C D E F G H I }
tuple_impls! { A B C D E F G H I J }
tuple_impls! { A B C D E F G H I J K }
tuple_impls! { A B C D E F G H I J K L }

Playground

Some Guy
  • 15,854
  • 10
  • 58
  • 67
2

No. (unless you are ready to use macros - see other answers)

Tuples in Rust have a fixed length just like arrays. I do not believe you can express the concept of "tuples of any length".

In other words a tuple of 2 elements is a different compound type than a tuple of 3 elements. If you insist on using tuples you have to go for the solution you outlined above.

Use a collection? (such as aliasing an existing one and implementing the trait on it)?

Bruno Grieder
  • 28,128
  • 8
  • 69
  • 101