Flatten nested tuples


#1

I want to achieve the following:

assert_eq!((1, 2).flatten(), (1, 2));
assert_eq!((1, (2, 3)).flatten(), (1, 2, 3));
assert_eq!((1, (2, (3, 4))).flatten(), (1, 2, 3, 4));
// ...

Due to conflicting trait implementations issues (stackoverflow) I tried to use #![feature(specialization)] and came up with this code (playground):

#![feature(specialization)]

trait Flatten {
    type Output;
    fn flatten(self) -> Self::Output;
}

impl<T, U> Flatten for (T, U) {
    default type Output = (T, U);
    default fn flatten(self) -> Self::Output {
        self
    }
}

impl<A, B, T, U> Flatten for (A, B) where B: Flatten<Output = (T, U)> {
    type Output = (A, T, U);
    fn flatten(self) -> Self::Output {
        let (a, b) = self;
        let (t, u) = b.flatten();
        (a, t, u)
    }
}

but then I get this error I’m not able to resolve:

expected associated type, found tuple

How could I make the tests run through?


#2

Not a direct answer to your question but, could a macro do what you want?
I’m mostly curious to know if that’s possible.


#3

I think so but I always try to avoid macros in favor of generic impls.


#4

That’s because you can theoretically specialize Output and flatten independently so they can’t depend on each other (eventually, there needs to be a way to bind them together but we don’t have that yet). You can do this by doing a fancy associated type dance but it gets messy pretty quickly.

If you’re interested, take a look at the Zip implementation: https://doc.rust-lang.org/src/core/iter/mod.rs.html#714-720


#5

By stupidly following the instructions of the compiler I came up with this piece of code:

impl<T, U> Flatten for (T, U) {
    default type Output = Self;
    default fn flatten(self) -> Self::Output {
        unsafe {
            ::std::mem::transmute::<Self, Self::Output>(self)
        }
    }
}

Still doesn’t compile…

transmute called with differently sized types:
(T, U) (size can vary because of T) to <(T, U) as Flatten>::Output (size can vary)

Why is Self another size then Self::Output when flattens Output is defined as Self? Am I heading in the right direction?