1.84K subscribers
3.23K photos
127 videos
15 files
3.52K links
Блог со звёздочкой.

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

Небольшое прикольное комьюнити: @decltype_chat_ptr_t
Автор: @insert_reference_here
Download Telegram
Блог*
#prog #rust #article Rust web frameworks have subpar error reporting Автор излагает свои требования к репорту и обработке ошибок (TL;DR: ошибки не должны теряться ни для клиента веб-сервера, ни для админа веб-сервера), показывает, как в этом отношении не…
#prog #rust #rustlib #article

biscotti, a new crate for HTTP cookies

Библиотека для работы с cookie. В статье-анонсе автор объясняет, почему он решил сделать новую библиотеку вместо того, чтобы использовать де-факто стандартную cookie.

* В отличие от cookie-rs, в biscotti используются разные типы для cookie запроса и cookie ответа сервера. В cookie-rs используются один и тот же тип, где у cookie от клиента все опциональные поля выставлены в None.
* biscotti поддерживает обработку нескольких cookie с одинаковыми именами в рамках одного запроса/ответа. В cookie-rs при обработке cookie с одинаковыми именами запоминаются только последняя информация, связанная с конкретным именем (иными словами, в cookie-rs map, в biscotti multimap).
* Централизованная обработка шифрования cookie и, как следствие, встроенная поддержка ротации ключей.

Разумеется, это не полная альтернатива: автор намеренно делал либу для сервера и потому biscotti не годится для работы с cookie на стороне клиента.
🔥1
#prog #rust #rustlib #article

Getting meaningful stack traces from Rust tests returning a Result

TL;DR: конвертация в операторе ? имеет возможность для настройки путём реализации From/Into для типа ошибки. Это можно использовать для того, чтобы сделать специальный тип ошибок, который паникует в реализации From::from, а за счёт #[track_caller] позволяет даже без бектрейса отловить место, где была создана ошибка.

Великие умы мыслят одинаково, так что автор обнаружил, что на crates.io уже есть крейт testresult, который реализует идею. Тем не менее, там были возможности для улучшения, так что в итоге он сделал PR (главным образом — добавление #[track_caller], но также и другие изменения для повышения полезности).
🥰3
#prog #rust #rustlib

coffee_break

Nowadays, the devious Rust compiler developers are trying to make the compiler even faster, and they're slowly succeeding.

Here comes Coffee break, a friendly developer tool to make this problem go away.


Before you run cargo build, reward yourself with a coffee break:

use coffee_break::coffee_break;

fn work_stuff() {
let maybe = |(true|false)||(false|true)||(true|false)|true;
let absolutely = maybe(true)(true)(true);

// Take a break and hit compile
coffee_break!(5 minutes);
}
You just got 5 minutes to stretch your legs, get a coffee, or make Friday afternoon a bit nicer.
👏7😁3
#prog #rust #rustlib

qualifier_attr — procedural macro attributes for adding "qualifiers" to various items.

// We can add a qualifier to a function
// with an attribute.
#[qualifiers(const)]
fn const_fn() -> u32 {
42
}

const CONST_RES: u32 = const_fn();

// It's not so impressive on its own,
// but with `cfg_attr`, it can be conditional.
#[cfg_attr(
feature = "extern_c",
no_mangle,
qualifiers(pub, extern "C")
)]
fn extern_c_fn() -> u32 {
42
}
👍9
#prog #rust #rustlib

morph-rs — библиотека для морфологического анализа русского языка.

Осторожно, распространяется под нестандартной лицензией.
❤‍🔥6🤔4
#prog #rust #rustlib

Стандартная библиотека Rust предоставляет типы Path и PathBuf, которые инкапсулируют в себе платформо-специфичные пути в файловой системе. Эти пути не обязательно закодированы в UTF-8, поэтому работать с Path не очень удобно: Path не реализует Display, множество полезных функций для манипуляции строками доступны только на str, но конвертация из Path в str может вернуть ошибку.

Подход std правилен в том смысле, что не делает каких-то предположений о содержимом файловых путей, но на практике подавляющее большинство путей и так в UTF-8, а некоторые программы вообще с не-UTF-8 путями не работают, поэтому такая педантичность мешается.

Библиотека camino позволяет сделать работу с путями более эргономичной. Именно, она предоставляет типы Utf8Path и Utf8PathBuf, которые повторяют функциональность Path и PathBuf, но гарантированно в UTF-8. Эта проверка осуществляется только один раз: во время создания этих типов, поэтому преобразование из них в str всегда успешно, и вдобавок они реализуют Display.

Если что, camino используется в rust-analyzer.

И ещё: если вам требуется отобразить путь при помощи Display, то вам пригодится метод display на Path. Он возвращает тип, который по возможности отображает путь так же, как и str, и каким-то образом замещает невалидные UTF-8 последовательности.
👍15🤡5🔥1💩1
#prog #rust #rustlib

pre — библиотека для помощи с правильным использованием unsafe кода.

Причина, по которой unsafe может быть в публичном интерфейсе функции, заключается в том, что для корректной работы функции требуются условия, которые не могут быть проверены компилятором или которые проверять очень дорого. В среде разработки на Rust распространена культура использования safety comments. Именно, unsafe функция перечисляет в документации необходимые условия для безопасного использования, а вызовы этой функции перечисляют причины, по которым условия удовлетворены. В clippy есть линт на отсутствующую safety документацию на unsafe определениях, а в tidy (внутреннем линтере для компилятора Rust) есть даже проверка на наличие этих комментариев на вызывающей стороне.

Не смотря на то, что это лучше, чем ничего, это подход довольно хрупок. Помимо того, что не все используют clippy (и уж тем более никто не использует tidy вне разработки самого Rust), эти проверки недостаточно гранулярны: они проверяют лишь наличие комментариев но не их содержимое. Это означает, что если по каким-то причинам условия использования и их обоснования на месте вызова разъедутся, эти инструменты их не отловят. Ну и, разумеется, никакие линтеры by design не могут остановить компиляцию кода (если только не включить их в общий процесс сборки кода). Библиотека pre стремится заполнить этот пробел.

Именно, библиотека позволяет добавлять на unsafe функции атрибуты #[pre], каждый из которых содержит одно условие — в простейшем виде просто в виде словесного описания:

#[pre("`arg` is a meaningful value")]
fn foo(arg: i32) {
assert_eq!(arg, 42);
}

Эти условия трансформируются в дополнительный аргумент для функции — типа с нулевым размером — значение которого предоставляется на вызывающей стороне при помощи макроса assures:

#[pre] // Enables `assure`ing preconditions inside the function
fn main() {
#[assure("`arg` is a meaningful value", reason = "42 is very meaningful")]
foo(42);
}

Подобный подход всё так же полагается на человека в плане непосредственно проверки условий, но за счёт проверки того, что каждое отдельное условие удовлетворено на вызывающей стороне с идентичной формулировкой позволяет синхронно развивать определение unsafe кода и его использование. Именно, за счёт того, что каждое условие становится частью аргумента, изменение списка условий (добавление, удаление или изменение формулировки) становится ошибкой компиляции.

Условия можно записывать не только словесно, но и также в виде предиката или специального синтаксиса для утверждения корректного выравнивания сырых указателей или их валидности для операций чтения и/или записи:

#[pre(valid_ptr(ptr_name, r+w))]
fn foo(ptr_name: *mut i32) {}

#[pre(proper_align(ptr_name))]
fn bar(ptr_name: *mut i32) {}

// Предусловие в виде предиката также
// добавляет в функцию соответствующий
// `debug_assert!`
#[pre(a < b || b > 17)]
fn baz(a: i32, b: i32) {}

Также библиотека даёт способ предоставить предусловия для функций из сторонних библиотек.

К сожалению, на сегодняшний день у макросов имеется множество ограничений, которые в основном выражаются в их меньшей полезности на стабильной версии компилятора. Ну и, разумеется, автор советуют не использовать pre безусловно ввиду очевидного влияния на время компиляции и на потребительский код в случае использования pre в публичных интерфейсах.
👍5🤔2
#prog #rust #rustlib #menacingopensource

Quickly prototype procedural macros using JavaScript or TypeScript!

crates.io/crates/js-macros
👍5
#prog #rust #rustlib

Jiff — библиотека для работы с датой и временем от BurntSushi.

Jiff is a datetime library for Rust that encourages you to jump into the pit of success. The focus of this library is providing high level datetime primitives that are difficult to misuse and have reasonable performance.

Наиболее заметным отличием Jiff является возможность работать с разницей моментов не только в терминах абсолютных отрезков времени, но и в терминах календарных единиц (дни и месяцы, например). Также Jiff поддерживает арифметику дат с учётом прыжков во времени из-за daylight saving time.

Для того, чтобы определить, насколько Jiff подходит для ваших задач, советую почитать философию дизайна и сравнение с существующими библиотеками.
👍3🔥2😐1
#prog #rust #rustlib

griddle — библиотека, которая реализует hash map с амортизированной по операциям вставки расширением размера. Может быть полезна для случаев, когда важна маленькая tail latency. Реализована не с нуля, а с помощью "сырого" API hashbrown.

На графике бенчмарк операций вставки для HashMap из std и griddle::HashMap.
🔥7
#prog #rust #rustlib #article

Designing A Fast Concurrent Hash Table

Статья о дизайне papaya, многопоточной в основном lock-free хэш-мапы с упором на пропускную способность на операциях чтения из мапы и предсказуемые задержки (без пиков). Также пока что является, кажется, единственной подобной структурой данных, которую можно использовать из асинхронного кода.

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

Так как многопоточная мапа по определению не может иметь аналог entry API (по крайней мере, без кучи тонкостей из-за потенциальных гонок с другими потоками), papaya предоставляет операции по атомарной модификации отдельных записей, использующие переданные коллбеки для выполнения нужной операции. Для наиболее ходовых операций есть отдельные методы, а для тех, которые в эти методы не укладываются, есть операция compute. Коллбек, переданный в этот метод, должен возвращать значение типа Operation, описывающий требуемое действие:

enum Operation<V, T> {
Insert(V),
Remove,
Abort(T),
}


В конце автор сравнивает свою реализацию с другими подобными библиотеками для Rust и поясняет, почему может иметь смысл использовать их вместо papaya.
🔥75
#prog #rust #rustlib

walkdir — кросс-платформерная библиотека для рекурсивного обхода директорий от Andrew Gallant aka Burntsushi.

use walkdir::WalkDir;

for entry in WalkDir::new("foo").min_depth(1).max_depth(3) {
println!("{}", entry?.path().display());
}


Библиотека умеет обнаруживает циклы символьных ссылок и сообщать в этих случаях об ошибке.

Небольшое неудобство заключается в том, что .filter() на итераторе не будет предотвращать заход в пропущенные директории, для этого нужно на итераторе вызвать .filter_entry(). Пример из документации для пропуска скрытых файлов на *nix-системах:

use walkdir::{DirEntry, WalkDir};

fn is_hidden(entry: &DirEntry) -> bool {
entry.file_name()
.to_str()
.map(|s| s.starts_with("."))
.unwrap_or(false)
}

for entry in WalkDir::new("foo")
.into_iter()
.filter_entry(|e| !is_hidden(e)) {
println!("{}", entry?.path().display());
}
👍9❤‍🔥4
#prog #rustlib

include-utils — библиотека для включения в исходники только части текста из указанного файла. Макрос include_md! также позволяет ссылаться на нужные части файла не через номера строк, а через указание секций, как это сделано в mdbook.
👍3🤯31
#prog #rust #rustlib

embed_it — макрос, который позволяет включить в бинарь целую директорию ресурсов и потом обращаться к вложенным директориям и файлам по именам, причём как по статическим (в виде геттеров с теми же именами, что и файлы), так и по рантаймовым. Посмотрите пример в README.
👍6
#prog #rustlib #article

parser combinators with nom 8 are here!

(восьмая мажорная версия — это прям дофига для Rust-библиотек)

Geoffroy Couprie рассказывает о новой архитектуре nom. В этой версии он позаимствовал из chumsky подход к архитектуре парсеров. Про этот подход ещё в 2022 году писал Niko Matsakis в статье Many modes: a GAT pattern, которая демонстрировала, как GAT могут пригодиться в реальных библиотеках, и таким образом обосновывала необходимость иметь GAT в языке.

В nom 8 парсером является то, что реализует трейт Parser. Вот как выглядит его определение за вычетом методов с реализацией по умолчанию:

trait Parser<Input> {
type Output;
type Error: ParseError<Input>;

fn process<OM: OutputMode>(
&mut self,
input: Input,
) -> PResult<OM, Input, Self::Output, Self::Error>;
}


Параметр OM реализует трейт OutputMode, реализации которого являются фактически type-level конфигом парсера:

trait OutputMode {
type Output: Mode;
type Error: Mode;
type Incomplete: IsStreaming;
}


Трейт Mode является ключом к новым возможностям. Реализации этого трейта позволяют абстрагироваться над "выходом парсера, имеющего отношение к T":

trait Mode {
type Output<T>;
// методы для комбинирования и преобразовывания Output
}


Для чего всё это нужно? Дело в том, что в nom есть комбинаторы, которые используют переданный аргументом парсер, но при этом не используют то, что этот парсер выдаёт в качестве результата. В качестве примера таких комбинаторов можно назвать preceded и delimited. Если конструирование значения является дорогим (скажем, это условный separated_list0, который собирает результаты разбора под-парсеров в вектор), то запускать парсер только ради того, чтобы отбросить его значение, будет довольно расточительно.

В самом nom определены два типа, которые реализовывают Mode: Emit и Check. Emit реализовывает Mode с type Output<T> = T, то есть парсер работает, как обычно. Check же реализовывает Mode с type Output<T> = (), то есть парсер работает, но отбрасывает результаты. Именно такое поведение годится для комбинаторов, которым не нужен результат под-парсера. Так как конечный парсер имеет методы, обобщённые по параметру OM, реализующему OutputMode, парсер не может сконструировать результат напрямую, а вынужден использовать для этого методы Mode, определённые на OM::Output. В случае Emit эти методы просто применяют переданные функции к аргументам, а для Check эти методы просто дропают переданные функции и возвращают ().

Так как ошибка парсера тоже может дорого конструироваться, тип OutputMode::Error по аналогичным причинам также реализует Mode. Это нужно, например, для opt, который делает парсер опциональным и по понятным причинам отбрасывает и разобранный результат, и ошибку.

Как вы могли заметить, у OutputMode также есть параметр Incomplete, реализующий трейт IsStreaming. Он отвечает за то, является ли недостаточный вход фатальной ошибкой или же одним из вариантов ошибки, от которой можно восстановиться. Раньше почти каждый базовый комбинатор nom существовал в двух версиях: streaming и complete. Это было нежелательно по нескольким причинам, главная из которых — разные сорта парсеров очень легко перепутать при написании своих парсеров и потом долго ловить непонятные ошибки. Ещё одним нежелательным следствием этого подхода стало, очевидно, дублирование кода. Разные версии этих парсеров по разному обрабатывали неполный вход, но за вычетом этого аспекта логика там была одинаковая.

Новый подход позволяет написать ветвление по OM::IsIstreaming::is_streaming() и обработать оба варианта в одной функции, естественным образом собирая разные варианты реагирования на ошибку неполного входа в одном месте. Так как в nom оба типа, реализующих IsStreamingStreaming и Complete — в соответствующих методах просто возвращают true и false соответственно, компилятор может увидеть ветвление по константному значению и просто убрать одну из веток, избежав генерации ненужного кода.
🔥7🎉2👌1
#prog #rust #rustlib

eval-macro — макрос, позволяющий генерировать код с использованием обычного кода на Rust. Посмотрите пример по ссылке.

Реализовано довольно проклято:

The content inside eval! is pasted into the main function of a temporary Rust project created in $HOME/.cargo/eval-macro/<project-id>. This project is created, compiled, executed, and removed at build time, and its stdout becomes the generated Rust code
🌚15🥴11🤯1
#prog #rust #rustlib

seq — a seq! macro to repeat a fragment of source code and substitute into each repetition a sequential numeric counter.

use seq_macro::seq;

fn main() {
let tuple = (1000, 100, 10);
let mut sum = 0;

// Expands to:
//
// sum += tuple.0;
// sum += tuple.1;
// sum += tuple.2;
seq!(N in 0..=2 {
sum += tuple.N;
});

assert_eq!(sum, 1110);
}


Написано, конечно же, Толяном.
8🤡1😐1
#prog #rust #rustasync #rustlib

async-std официально deprecated. Взамен советуют использовать smol и смежные крейты.

ДАВНО ПОРА
👍7😭5🔥2🥰1😁1
#prog #rust #rustlib #article

Introducing facet: Reflection for Rust

Как сказано в facet.rs:

the last proc macro / the last derive you’ll ever need


Сердце библиотеки — трейт Facet и derive-макрос для него. В отличие от других крейтов, которые ползают по определениям типов, facet генерирует не код обхода значений, а константы, которые описывают формы значений и потому могут быть утилизированы разными библиотеками разными способами. Из примеров: (де)сериализация, отладочная печать, ассерты с диффами (которые не полагаются на пост-обработку Debug-выхлопа). В силу того, что код не генерируется, эти реализации могут использовать нерекурсивные алгоритмы и таким образом избежать переполнение стека и легко регулировать глубину вложенности.

В статье рассказывается, зачем это создано и какие ещё преимущества даёт.
👍125🔥2🤡1