1.83K subscribers
3.3K photos
131 videos
15 files
3.57K links
Блог со звёздочкой.

Много репостов, немножко программирования.

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
Forwarded from ruDALL-E Malevich (XL)
Фото сгенерировано моделью ruDALL-E от Сбера по запросу "Вафелька"
Forwarded from USSResearch
Eli Albert Loeb из Калифорнии написал или написала любопытную бакалаварскую работу (чтобы все бакалавры в России писали такие работы) про маскулиность гопников и истоки этого явления.
Почитать можно совершенно бесплатно
Блог*
Редкие эпизоды из абсурдной жизни программиста. "Две пиццы — это не про размер команды, а про размер фонда оплаты труда" @topizza #blogrecommendation
К сожалению, больше не могу рекомендовать. Мало то, что контент пошёл куда-то не туда, так ещё и в последние пару дней там начался откровенный скам.
Блог*
#prog #rust #моё Допустим, нам нужно проанализировать большой JSON, и нам нужно вытащить часть полей с конкретными типами, но по возможности оставить остальные. При этом этот наборы полей в разных местах немного разные, так что повторять себя не хочется.…
#prog #rust #моё

При отладке подобного кода — с гетерогенными списками — может быть полезным иметь возможность их распечатывать. Казалось бы, фигня вопрос: просто навесим #[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 руками.
Но как именно это сделать? Первый способ, лежащий на поверхности (aka способ ленивого программиста) — собрать в массив вектор элементы гетерогенного списка, ослабленные до 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,
[],
),
),
]

Гораздо лучше.
Но действительно ли нам нужно собирать всё в один вектор и лишний раз выделять память? Мы же можем поступить так же, как и реализация Debug для слайса: воспользоваться хелпером std::fmt::DebugList и скормить ему элементы списка. Единственной сложностью тут выступает то, что мы, очевидно, не можем (ну то есть можем, но не хотим, потому что это потребует написания дофига кода и будет неэффективно в рантайме) воспользоваться DebugList::entries, так что придётся скармливать элементы по одному при помощи DebugList::entry.

Снова выделим общий функционал в отдельный трейт:

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, так что используйте их, пожалуйста.

P. S.: как всегда, весь код в гисте.
За час с момента публикации пост расшарили трижды, но ни одного комментария не последовало. Всё и так понятно или всё совсем непонятно? 🤔
#prog #rust

В каком-то смысле костыль, но штука нужная
Наткнулся в твиттере на анонс интересного инструмента:

> cargo-hakari

Он автоматизирует создание и поддержку "workspace-hack" крейтов, ускоряя компиляцию для больших воркспейсов. Что такое "workspace-hack" и зачем его используют в rustc и firefox — читайте в треде.
Вы не поверите, но это японские блины. Их подают в Crepes de Anne в Нагое.
Forwarded from Puh
Как сделать ноготочки на типах rust
#prog #rust #rustlib #serde

Трейт Deserializer имеет методы вида deserialize_type. Трейт Serializer имеет методы вида serialize_type. Что это значит? Это значит, что одно можно непосредственно сцепить с другим, и таким образом перегонять данные из одного формата данных, поддерживаемых serde, в другой, никогда не материализуя экземпляры соответствующих типов в памяти целиком. Собственно, именно это и делает serde-transcode.

Пример из документации (преттификация JSON):

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();
}
Сделать, что ли, отдельный хештег для serde 🤔
Forwarded from Санечка Ъысь (Anna Weiss)
Я уволился.