
Size: a a a







#[derive(Debug)] — и дело с концом:#[derive(Debug)]
struct HNil;
#[derive(Debug)]
struct HCons<H, T> {
    head: H,
    tail: T,
}
Но всё не так радужно. Попробуем распечатать hlist![42, 'b', "sup", ((), (3.4, Vec::<i32>::new()))]:HCons { head: 42, tail: HCons { head: 'b', tail: HCons { head: "sup", tail: HCons { head: ((), (3.4, [])), tail: HNil } } } }
Ну... Выглядит не особо читаемо. Но ведь если использовать "{:#?}" в качестве форматной строки, станет лучше, ведь так?HCons {
    head: 42,
    tail: HCons {
        head: 'b',
        tail: HCons {
            head: "sup",
            tail: HCons {
                head: (
                    (),
                    (
                        3.4,
                        [],
                    ),
                ),
                tail: HNil,
            },
        },
    },
}
Нет, не стало. Мало того, что детали реализации просачиваются в вывод и замусоривают его, так ещё и добавляется по уровню вложенности на каждый элемент логически плоской последовательности. Вывод однозначен: нужно писать реализацию Debug руками.
dyn Debug, и попросту отформатировать этот вектор.HNil и HCons — разные типы, нам нужно абстрагировать в трейт общую для них операцию, а именно — добавление элементов в переданный вектор. Как должна выглядеть сигнатура метода? Кажется, что как-то так:fn append_fmt_items(&self, items: &mut Vec<&dyn Debug>)
...но это так не работает. Дело в том, что сигнатура говорит о том, что времена жизни ссылки на self и ссылок на трейт-объекты не связаны друг с другом. Но это явно не так, ведь мы собираемся класть в items данные из self! Что ж, выразим этот факт в коде:trait AppendFmt {
    fn append_fmt_items<'a>(&'a self, items: &mut Vec<&'a dyn Debug>);
}
Теперь напишем реализации для компонент списка:impl AppendFmt for HNil {
    fn append_fmt_items<'a>(&'a self, _: &mut Vec<&'a dyn Debug>) {
        /* nothing to do here */
    }
}
impl<H, T> AppendFmt for HCons<H, T>
where
    H: Debug,
    T: AppendFmt,
{
    fn append_fmt_items<'a>(&'a self, items: &mut Vec<&'a dyn Debug>) {
        items.push(&self.head);
        self.tail.append_fmt_items(items);
    }
}
Теперь мне бы очень хотелось написать impl<T: AppendFmt> Debug for T... но, к сожалению, мне не даст компилятор, потому что это будет перекрываться с impl<T: Debug> Debug for &'_ T. Так что, стиснув зубы, пишем руками:impl Debug for HNil {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let list: &[&dyn Debug] = &[];
        list.fmt(f)
        // или просто f.write_str("[]")
    }
}
impl<H, T> Debug for HCons<H, T>
where
    Self: AppendFmt,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        let mut items = Vec::new();
        self.append_fmt_items(&mut items);
        items.fmt(f)
    }
}
Проверив, снова распечатав hlist![42, 'b', "sup", ((), (3.4, Vec::<i32>::new()))]:[42, 'b', "sup", ((), (3.4, []))]
А если попросить "красивую" печать?[
    42,
    'b',
    "sup",
    (
        (),
        (
            3.4,
            [],
        ),
    ),
]
Гораздо лучше.
trait HlistFmt {
    fn finish_fmt(&self, list: &mut DebugList<'_, '_>) -> fmt::Result;
}
Реализуем для HNil и HCons:impl HlistFmt for HNil {
    fn finish_fmt(&self, list: &mut DebugList<'_, '_>) -> fmt::Result {
        list.finish()
    }
}
impl<H, T> HlistFmt for HCons<H, T>
where
    H: Debug,
    T: HlistFmt,
{
    fn finish_fmt(&self, list: &mut DebugList<'_, '_>) -> fmt::Result {
        list.entry(&self.head);
        self.tail.finish_fmt(list)
    }
}
Теперь через это реализуем Debug:impl Debug for HNil {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.finish_fmt(&mut f.debug_list())
        // или опять f.write_str("[]")
    }
}
impl<H, T> Debug for HCons<H, T>
where
    Self: HlistFmt,
{
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.finish_fmt(&mut f.debug_list())
    }
}
Если мы теперь распечатаем тот же самый пробный список, то мы получим те же самые результаты, так что показывать это я не буду.DebugList — не единственный хелпер для форматирования в std, есть также DebugMap, DebugSet, DebugStruct и DebugTuple, суть функционала которых ясна из названий. Эти вещи сильно облегчают написание кастомных реализаций Debug, так что используйте их, пожалуйста.







extern crate serde;
extern crate serde_json;
extern crate serde_transcode;
use serde::Serialize;
use serde_json::{Serializer, Deserializer};
use std::io::{Read, Write, BufReader, BufWriter};
use std::fs::File;
fn main() {
    let reader = BufReader::new(File::open("input.json").unwrap());
    let writer = BufWriter::new(File::create("output.json").unwrap());
    let mut deserializer = Deserializer::from_reader(reader);
    let mut serializer = Serializer::pretty(writer);
    serde_transcode::transcode(&mut deserializer, &mut serializer).unwrap();
    serializer.into_inner().flush().unwrap();
}
