
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();
}