问题
In Rust, is there any way to use trait
s and impl
s 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