#generics #rust
#Ржавчина #кортежи #универсальное программирование
Вопрос:
В Rust есть ли какой-либо способ использовать trait
s и impl
s для (рекурсивного) выравнивания кортежей?
Если это поможет, то хорошим началом будет что-то, что работает с N вложенными парами
trait FlattenTuple {
fn into_flattened(self) -> /* ??? */
}
// such that
assert_eq!((1, (2, 3)).into_flattened(), (1, 2, 3))
Было бы еще лучше, если бы можно было расширить работу с любым типом вложенного кортежа таким образом, чтобы:
assert_eq!(((1, 2), 2, (3, (4, 5))).into_flattened(), (1, 2, 2, 3, 4, 5))
Ответ №1:
Возможно для определенных небольших определений «сглаживать», но реально не совсем.
Начните с наиболее конкретной реализации:
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)
}
}
Тогда сделайте это немного более универсальным:
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)
}
}
А затем повторите для каждой возможной перестановки:
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)
}
}
Эти две реализации охватывают два ваших случая.
Однако затем вам пришлось бы перечислять каждый желаемый тип ввода, вероятно, с помощью генерации кода. Насколько мне известно, нет способа «проверить» тип ввода, а затем «объединить» его с типом вывода.
Вы даже можете попытаться написать что-нибудь несколько рекурсивное:
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)
}
}
Но это быстро приведет к проблемам базового варианта: терминал 42
не реализует FlattenTuple
, и если вы попытаетесь impl<T> FlattenTuple for T
, вы столкнетесь с конфликтующими реализациями признаков.
Комментарии:
1. Спасибо. Прежде чем я опубликовал свой вопрос, я попробовал несколько разных способов, но продолжал сталкиваться с проблемой конфликтующих реализаций. Например, реализация
for (A, B)
означает, что вы не можете реализовать для(A, (B, C))
и(A, (B, (C, D)))
и так далее для решенияN
случая вложенных пар, и я не уверен, что ситуация изменится с предложениями where .2. @lloydmeta: Да, эта конфликтующая реализация действительно раздражает : (
3. @MatthieuM. вы знаете, что для нашего кода гораздо лучше не быть двусмысленным. Возможно, специализация решит эту проблему, а может и нет…
4. @Shepmaster: Я согласен с отсутствием двусмысленности, но я так часто использовал C в прошлом, что специализация укоренилась в моем мозгу ^^