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

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

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
#blogrecommendation не глядя
Итак, я всё-таки сделал себе блог! Теперь он доступен по адресу ihatereality.space.

И там уже есть моя статья “Why null sucks, even if it's checked”!

Блог работает на Zola и GH pages. Все сорцы открытые, если вдруг кто-то хочет посмотреть/стырить мои настройки =)
#prog #rust

Rust и Вафель не перестают меня удивлять.

В Rust есть паттерны. Они есть (в том числе) для массивов и слайсов. Если у нас есть массив на пять элементов, то мы можем переместить из него первый и последний элементы вот так:

let [first, _, _, _, last] = arr5;

В Rust также есть паттерны для слайсов, которые позволяют матчиться по последовательностям произвольной длины. Характерной их особенностью является возможность использовать паттерн .. для обозначения "все оставшиеся элементы слайса". Выглядит это примерно так:

fn trim_ends<T>(arr: &[T]) -> Option<&[T]> {
match arr {
[_, rest @ .., _] => Some(rest),
_ => None,
}
}


Очевидно, имя, привязываемое к .. внутри паттерна слайса, имеет тип &[T]. Однако .. можно использовать и для паттернов массивов! В этом случае имя, привязываемое к этому паттерну, будет иметь тип массива, содержащего все остальные элементы:

struct Foo;

fn main() {
let [_, rest @ .., _] = [Foo, Foo, Foo, Foo];
let _: [Foo; 2] = rest; // тайпчекается
}


Вот тебе и арифметика на массивах!

(thanks @wafflelapkin)
Хотите быстро инициализировать переменные в Rust? Просто используйте @!

let a @ b @ c @ d @ e = 0;

Это реально работает.
#rust be like:

for h in self.tcx.hir().parent_iter(param.hir_id) {
break 'origin match h.1 {
// несколько пропущенных веток
_ => continue,
};
}

Здесь
> уезжаю на работу слишком рано, чтобы забрать доставку из пункта выдачи
> приезжаю с работы слишком поздно, чтобы забрать доставку из пункта выдачи

😒
> мне не нравится реальность

Как вообще можно не любить реальность, в которой есть Вафелька?
Как политкорректно сказать коллеге (бывшему сишнику), что он пишет лютый говнокод?
Правительство РФ завело свой канал в Telegram: t.iss.one/government_rus.

У меня всё.
В СМЫСЛЕ УЖЕ СЕНТЯБРЬ
Занимательная статья об оптимизации CRDT (Conflict-free replicated data type, один из многих вариантов реализации конкурентного редактирования).

5000x faster CRDTs: An Adventure in Optimization

Там есть немного раста, много js-а и всякие занимательные штуки, рекомендую к прочтению :p
#prog #rust #моё

Допустим, нам нужно проанализировать большой JSON, и нам нужно вытащить часть полей с конкретными типами, но по возможности оставить остальные. При этом этот наборы полей в разных местах немного разные, так что повторять себя не хочется. Можем ли мы сделать лучше, чем выписывать тип под каждую комбинацию полей? Оказывается, да!

И благодаря чуду serde для этого нам понадобится совсем немного кода. Один из атрибутов, который можно повесить на поле — это #[serde(flatten)]. Применение этого атрибута поднимает поля помеченного поля на уровень выше в (де)сериализованном представлении:

use serde::Deserialize;

#[derive(Deserialize)]
struct Inner {
items: Vec<u32>,
}

#[derive(Deserialize)]
struct Outer {
foo: String,
#[serde(flatten)]
inner: Inner,
}

fn main() {
let input = r#"{
"foo": "bar",
"items": [0, 1, 2]
}"#;
assert!(serde_json::from_str::<Outer>(input).is_ok());
}

Этот атрибут работает и для обобщённых типов. Конечно, мы можем сделать типы для пар, троек, четвёрок и так далее типов... Но мы не хотим повторять себя! А потому воспользуемся старым трюком из функционального программирования: гетерогенными списками.

struct HNil;

#[derive(Deserialize)]
struct HCons<H, T> {
#[serde(flatten)]
head: H,
#[serde(flatten)]
tail: T,
}

Набор типов для десериализации мы будем конструировать при помощи вложенных HCons-ов, а для удобства выписывания этого типа воспользуемся макросом:

macro_rules! HList {
() => { HNil };
($head:ty $(, $rest:ty)* $(,)?) => { HCons<$head, HList![$($rest),*]> };
}

Как вы могли заметить, я не написал #[derive(Deserialize)] для HNil. Это сделано намерено, так как мы хотим для HNil не семантику десериализации юнит-структуры, а семантики "десериализуй сюда, что угодно". Для этого надо немного углубиться в то, как в serde происходит десериализация. Вот как выглядит определение Deserialize:

trait Deserialize<'de>: Sized {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: Deserializer<'de>;
}

Здесь 'de обозначает время жизни исходных данных. Этот параметр сделан для того, чтобы типы можно было десериализовывать из строки и при это хранить в них слайсы строк, а не String, хранящий память в куче. Deserializer же — это трейт для типов, которые предоставляют набор ручек вида "дай мне число" или "дай мне строку". Именно эти типы хранят в себе исходные данные, и именно эти типы описывают в конечном счёте, в каком именно формате записаны данные. В процессе эти типы оперируют посетителем: типом, реализующим Visitor, которые вынимает данные из десериализатора и непосредственно конструирует из полученных значений значение того типа, для которого реализовывается трейт Deserialize. Да, звучит немного запутанно, так что коротко повторим:

* Deserialize описывает общий интерфейс для десериализации типов (с поглощением исходных данных)
* Тип, реализующий Deserializer, описывает конкретный формат исходных данных
* Тип, реализующий Visitor, описывает, как десериализуется конкретный тип (и это описание в общем случае не поглощает вход напрямую и потому может быть скомпоновано с другими посетителями). Эти типы, как правило, являются типами нулевого размера и если и содержат поля, то только PhantomData для захвата обобщённых параметров.
Окей, но как же нам десериализовать HNil с нужной нам логикой? Так как JSON — самоописываемый формат в том смысле, что мы можем посмотреть на начало входа и решить, как парсить остаток — мы можем вызвать у десериализатора метод dezerialize_any, что фактически означает "посмотри данные и реши сам, как их разбирать". Нам также потребуется посетитель, который для разбора подчастей JSON-документа будет вызывать вне зависимости от запрашиваемого типа deserialize_any у десериализатора и не будет при это сохранять никаких данных. К счастью, в составе serde уже есть такой тип — IgnoredAny — так что мы можем просто использовать его:

impl<'de> Deserialize<'de> for HNil {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>
{
deserializer.deserialize_any(serde::de::IgnoredAny)?;
Ok(Self)
}
}

Как видите, код проверяет, что формат корректен, но не вынимает реально из него никаких данных — ровно то, что нам и было нужно.

Теперь мы сделаем несколько структур для десериализации — и макрос для более удобной деконструкции HList:

macro_rules! hlist_pat {
() => { HNil };
($head:pat $(, $rest:pat)* $(,)?) => { HCons { head: $head, tail: hlist_pat!($($rest),*) } };
}

#[derive(Deserialize)]
struct Items {
items: Vec<String>
}

#[derive(Deserialize)]
struct Version {
version: u32,
}

#[derive(Deserialize)]
struct User {
user_id: String,
}

Сконструируем вход, содержащий все поля:

let json = r#"{
"user_id": "john_doe",
"items": ["salad", "juice", "beer", "fork"],
"version": 0,
"bogus": [null, 0.3, 4, "nope", {}]
}"#;

И попробуем вытащить всё, что нас интересует:

use serde_json::from_str as from_json;
let hlist_pat!(user, items, version) =
from_json::<HList![User, Items, Version]>(json).unwrap();
assert_eq!(user.user_id, "john_doe");
assert_eq!(items.items, ["salad", "juice", "beer", "fork"]);
assert_eq!(version.version, 0);

Теперь уберём, скажем, версию:

let json = r#"{
"user_id": "john_doe",
"items": ["salad", "juice", "beer", "fork"],
"bogus": [null, 0.3, 4, "nope", {}]
}"#;

assert!(from_json::<HList![User, Items, Version]>(json).is_err());
assert!(from_json::<HList![User, Items ]>(json).is_ok());

Отсутствие user_id и items вызовет ошибку, но не в том случае, если требуемые наборы полей опциональны:

let json = r#"{
"version": 0,
"bogus": [null, 0.3, 4, "nope", {}]
}"#;

assert!(from_json::<HList![ User, Items, Version]>(json).is_err());
assert!(from_json::<HList![Option<User>, Option<Items>, Version]>(json).is_ok());

Ну и, разумеется, мы можем вытащить все остальные поля, что есть:

type Rest = serde_json::Map<String, serde_json::Value>;
let json = r#"{
"user_id": "john_doe",
"items": ["salad", "juice", "beer", "fork"],
"version": 0,
"bogus": [null, 0.3, 4, "nope", {}]
}"#;

let hlist_pat!(_, _, _, rest) = from_json::<HList![User, Items, Version, Rest]>(json).unwrap();
assert_eq!(
serde_json::Value::Object(rest),
serde_json::json!({
"bogus": [null, 0.3, 4, "nope", {}],
})
);

По моему, вышло красиво и изящно. Как всегда, весь код в гисте.
🔄