Are there any ways to recursively flatten tuples?

北战南征 提交于 2021-01-20 05:58:14

问题


In Rust, is there any way to use traits and impls to (recursively) flatten tuples?

If it helps, something that works with N nested pairs is a good start

trait FlattenTuple {
    fn into_flattened(self) -> /* ??? */
}

// such that
assert_eq!((1, (2, 3)).into_flattened(), (1, 2, 3))

It would be even better if it could be extended work with any kind of nested tuple such that:

assert_eq!(((1, 2), 2, (3, (4, 5))).into_flattened(), (1, 2, 2, 3, 4, 5))

回答1:


Maybe for certain small definitions of "flatten", but realistically not really.

Start with the most specific implementation:

trait FlattenTuple {
    fn into_flattened(self) -> (u8, u8, u8);
}

impl FlattenTuple for (u8, (u8, u8)) {
    fn into_flattened(self) -> (u8, u8, u8) {
        (self.0, (self.1).0, (self.1).1)
    }
}

Then make it a bit more generic:

trait FlattenTuple {
    type Output;
    fn into_flattened(self) -> Self::Output;
}

impl<A, B, C> FlattenTuple for (A, (B, C)) {
    type Output = (A, B, C);

    fn into_flattened(self) -> Self::Output {
        (self.0, (self.1).0, (self.1).1)
    }
}

And then repeat for every possible permutation:

impl<A, B, C, D, E, F> FlattenTuple for ((A, B), C, (D, (E, F))) {
    type Output = (A, B, C, D, E, F);

    fn into_flattened(self) -> Self::Output {
        ((self.0).0, (self.0).1, self.1, (self.2).0, ((self.2).1).0, ((self.2).1).1)
    }
}

These two implementations cover your two cases.

However, you'd then have to enumerate every input type you'd like, probably via code generation. There's no way I'm aware of to "inspect" the input type and then "splice" it into the output type.

You can even try to write something somewhat recursive:

impl<A, B, C, D, E, F> FlattenTuple for (A, B)
    where A: FlattenTuple<Output = (C, D)>,
          B: FlattenTuple<Output = (E, F)>,
{
    type Output = (C, D, E, F);

    fn into_flattened(self) -> Self::Output {
        let (a, b) = self;
        let (c, d) = a.into_flattened();
        let (e, f) = b.into_flattened();

        (c, d, e, f)
    }
}

But this will quickly run into base-case issues: the terminal 42 doesn't implement FlattenTuple, and if you try to impl<T> FlattenTuple for T you will hit conflicting trait implementations.




回答2:


I implemented this with specialization and auto trait.

docs.rs

github repo


Usage is basically

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

It removes all empty unit tuple () (like python).


If you are writing generic code, you need to add

where (A, B): Flatten

rustc wants this because Flatten is implemented only if length of output tuple is smaller than 13.



来源:https://stackoverflow.com/questions/40178572/are-there-any-ways-to-recursively-flatten-tuples

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!