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

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

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
Отлично, теперь переходим к самому вкусному: написанию макроса! Нам нужно разобрать match, так что начнём с этого:

macro_rules! ascii_case_insensitive {
(match $value:ident {
$(... ,)*
_ => $catch_all:expr $(,)?
}) => { ... }
}

А теперь на минуту остановимся и подумаем, что из себя представляет паттерн, который мы пытаемся разобрать. В прошлый раз я совершенно упустил из виду, что обычно мы можем перечислить несколько паттернов, разделив их |, равно как и то, что паттерн может также предваряться |. Таким образом, корректный кусок макроса для распознавания паттернов должен выглядеть так:

$(|)? $($pattern:literal)|+

$(|)? отвечает за опциональную черту в начале. $pattern:literal говорит, что $pattern — это литерал, а $(...)|+ говорит о том, что то, что внутри скобок, повторяется один или более раз, и что повторы разделены |. Но постойте-ка, есть же ещё и опциональное охранное выражение! С учётом всего этого паттерн для одной ветви принимает такой вид:

$(|)? $($pattern:literal)|+ $(if $condition:expr)? => $arm:expr,

Отлично, с разбором мы справились (правда, всё так же упустив возможность привязать имена к паттернам). Что мы со всем этим делаем? Мы проверяем, что все строки в нижнем регистре:

#[deny(const_err)]
const _ARE_ALL_ASCII_LOWERCASE: [(); 1] = [(); are_all_ascii_lowercase(&[$($($pattern,)+)*]) as _];

И что они все разные:

#[allow(dead_code)]
fn non_repeating(s: &str) {
#[deny(unreachable_patterns)]
match s {
$($(| $pattern)+ => (),)*
_ => (),
}
}

А что нам делать непосредственно самой проверкой? Мы проверяем, что значение равно, за вычетом ASCII-регистра, одному из паттернов... И что охранное выражение также справедливо, если оно есть:

x if ($(x.eq_ignore_ascii_case($pattern))||+) $(&& $condition)? => $arm,

Обратите внимание, здесь мы повторяем (+) выражения для паттернов, разделив их ||.

Что ж, давайте опробуем макрос в действии:

#[derive(Debug)]
enum Example {
Foo,
Bar,
FourtyTwo,
}

impl std::str::FromStr for Example {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
Ok(ascii_case_insensitive!(match s {
"foo" => Self::Foo,
"bar" if s.as_bytes()[0].is_ascii_lowercase() => Self::Bar,
"fourtytwo" | "fourty_two" | "42" => Self::FourtyTwo,
_ => return Err(s.into()),
}))
}
}

fn main() {
let inputs = [
"foo",
"Foo",
"FOO",
"bar",
"bAr",
"BAR", // ошибка, первый символ в верхнем регистре
"fourtytwo",
"Fourtytwo",
"FOURTYTWO",
"fourty_two",
"fOuRtY_tWo",
"42",
"bogus",
];
for &input in &inputs[..] {
println!("{:?}", input.parse::<Example>());
}
}

Эта программа выдаёт следующее:

Ok(Foo)
Ok(Foo)
Ok(Foo)
Ok(Bar)
Ok(Bar)
Err("BAR")
Ok(FourtyTwo)
Ok(FourtyTwo)
Ok(FourtyTwo)
Ok(FourtyTwo)
Ok(FourtyTwo)
Ok(FourtyTwo)
Err("bogus")

...как и ожидалось. Что будет, если мы попытаемся сделать два одинаковых паттерна? Скажем, так:

            ...
"foo" | "foo" => Self::Foo,
...

Компилятор жалуется:

error: unreachable pattern

А если один из паттернов не в нижнем регистре:

            ...
"Foo" => Self::Foo,
...

то компилятор опять жалуется:

error[E0308]: mismatched types

Всё работает, как и ожидалось! Как всегда, весь код в гисте.
Как же хочется временами простого человеческого "Вот тебе деньги, возьми их"
#prog #article

Обзор истории систем контроля версий с разбором их внутренних устройств в двух частях: первая, вторая.

В тему также интервью с Pierre-Étienne Meunier, ведущим разработчиком Pijul.
#prog #rust #article

Статья о том, чего бы автору хотелось видеть для Rust в 2021 году. Всё ключевые моменты выделять не буду (а иначе зачем я вам ссылку даю?), выделю только то, что привлекло моё внимание:

* I want to see Rust shed some of its reputation for being hard to learn

Согласно автору, новички сталкиваются с повышенной нагрузкой при изучении языка, поскольку им приходится учить и сам язык, и то, как писать на нём идиоматичный код. Учить Rust сложно ввиду того, что он схож с мейнстримными ЯП, но при этом довольно сильно от них отличается — достаточно, чтобы предыдущий опыт был не слишком полезен. Одним из следствий этих отличий является то, что лучшие практики из одних языков считаются антипаттернами в Rust (pub/sub, observer pattern), равно как и наоборот (затенение переменных). Автор считает, что новичкам имеет смысл позволить себе писать сначала неаккуратный и/или неидиоматичный код, а улучшать его уже потом. Да, это вопрос чисто психологический, но в интернете чаще выкладывают хороший код и редко — скажем так, не очень хороший, что создаёт определённое психологическое давление. Именно это и пишет автор:

I am not exactly sure how to create the conditions for this outcome. Maybe more people can publish more Rust that looks messy but “just works”.

Собственно, как совершенно справедливо заметил trentj на URLO (что в итоге стало фразой недели в TWiR №360):

"Just because Rust allows you to write super cool non-allocating zero-copy algorithms safely, doesn’t mean every algorithm you write should be super cool, zero-copy and non-allocating."

* More blog posts from developers and management using Rust at work

Особенно автору хочется видеть материалы с информацией о том, почему был выбран именно Rust, и о том, каков был опыт по сравнению с предыдущей реализацией — как правило, написанной на другом языке.

It’s great to hear Rust used in complicated, low-level development, but it would be normalizing to read about more trivial types of applications, just as a way to highlight Rust as being good for general purpose use.

Я со своей стороны могу лишь добавить, что по мере возможности стараюсь закрывать этот пробел в русскоязычном сообществе Telegram. Некоторые из подобных постов на моём канале можно найти по хештегу #successstory.

* More shared experiences from people picking up Rust as a second language.

Во многом перекликается с первым тезисом. Согласно автору, это может поспособствовать двум вещам: показать, что для того, чтобы выучить Rust, не нужно быть каким-то выдающимся человеком, и раскрыть глаза на вещи, которые опытные Rust-разработчики не замечают или воспринимают как должное.

I believe content from this crowd would be my favorites to read since I think they’ll give valid opinions to someone who has been using Rust for years (like me) now overlook or accept without second thoughts.
#prog #rust #article

Тем временем народ настолько звереет от нехватки анонимных сумм-типов в Rust (а RFC для них было немало — Вафель не даст соврать), что пишет свои. В этот раз получилось даже неплохо.
#prog #rust #article

Небольшая заметка о том, как можно ограничить видимость реализации трейта, используя исключительно имеющиеся возможности системы типов Rust.
#prog #go #article

Статья о внутреннем устройстве map в Go. К сожалению, в статье длиннющая преамбула о различных вариантах реализации хэш-таблиц в разных языках и крайне мало о собственно реализации в Go.

Ключевая фишка реализации — фактически нетипизированная реализация мапы как таковой, отвечающая структуре hmap. Поле buckets имеет тип unsafe.Pointer, который является аналогом void* из C: может указывать на что угодно. В контексте Go примечателен ещё и тем, что, в отличие от встроенных указателей, он не отслеживается сборщиком мусора. Все мало-мальски интересные функции, манипулирующие map, принимают также указатель на значение типа maptype. В нем описаны характеристики хэш-таблицы — такие, как размер ключей, значений, бакетов, хранятся ли они значения по месту или же хранятся лишь указатели на них, а также функция для хэширования ключей и (через поле тип type_) функции для сравнения значений ключей и значений. Откуда берутся значения для maptype? А их генерирует компилятор автоматически, когда переписывает обращения к map через функции типа mapaccess1/mapaccess2. На этапе же компиляции он может проверить, что для типа ключа map определены операции хэширования и сравнения.

Не знаю, как вам, а мне эта картина кажется весьма шаткой, особенно с учётом комментариев вроде "сохраняйте это определение структуры согласованным с вот этим местом в реализации рефлексии и той части компилятора, которая обходит AST". Ну и дублирование кода между mapaccess1/mapaccess2 не может не радовать.
Forwarded from Generative Anton (Anton Repushko)
Если быстро выдернуть чеку у гранаты и поднести ухо к дыре, где была чека, можно услышать, как увеличивается средний мировой IQ.
В разработке пост с рабочим названием "как быть, если тебе нужно написать простенький парсер, но nom выглядит перебором"
Forwarded from crates.io updates (crates.io update notifier bot)
Crate was updated: err_or#0.1.0 [docs.rs] [crates.io] [lib.rs]
Forwarded from мне не нравится реальность (вафель 🧇🍓)
А я тут маленький крейтик релизнул 👀

Он добавляет методы Option::{err_or,err_or_else} аналогичные к Option::{ok_or,ok_or_else}
xxx:

    // This call is safe since ...
unsafe { ... }

yyy:

.. tomorrow (простите)

#quotes #трудовыебудни
#prog #suckassstory

1С — это не просто язык, это образ мышления.
Forwarded from Санечка Ъысь (ǺʎĔĶ₡Ǻ)
Когда у меня спрашивают, почему я не люблю одинэсников. Хотя бы, блять, за это.
Из рабочей переписки. В общем, базу товаров синхронизируем в 1С и мускуле.
Санечка Ъысь
Когда у меня спрашивают, почему я не люблю одинэсников. Хотя бы, блять, за это. Из рабочей переписки. В общем, базу товаров синхронизируем в 1С и мускуле.
"1C — врождённое генетическое заболевание. Когда-нибудь генная инженерия позволит исправлять такие промахи при планировании беременности"

#quotes от подписчика, попросившего его (или её?) не называть
#prog #video

Доклад Юрия Богомолова о том, как применили схемы рекурсии (recursive schemes) применительно к реальной задаче. Неплохой пример практичности ФП (пусть и на немножко хреновом ЯП TypeScript).

Могу со своей стороны добавить, что введение, на мой взгляд вышло чересчур сжатым, так что если вы раньше не знали, что такое схемы рекурсии, то не факт, что понимания добавится именно после просмотра этого доклада