ar
Feedback
Блог*

Блог*

الذهاب إلى القناة على Telegram

Блог со звёздочкой. Много репостов, немножко программирования. Небольшое прикольное комьюнити: @decltype_chat_ptr_t Автор: @insert_reference_here

إظهار المزيد
1 950
المشتركون
+2124 ساعات
+227 أيام
+2230 أيام
أرشيف المشاركات
#prog #go #article How does Go calculate len()..? Статья о том, как встроенная функция Go len, применимая к пяти семействам типов (слайс, массив, строка, мапа, канал), преобразуется в конкретные операции для каждого типа — от парсинга до кодгена

photo content

#prog #meme

photo content

Обновил прошивку своего чипа

Антон: — Rust — простой язык, чего вы? Тоже Антон: <List as AsStrList<{ n_digits(<N as NumericValue>::VALUE) }>>::LIST

#prog #rust #моё В крейте pulldown-cmark есть структура Options, в которой хранятся опции для конфигурирования поведения парсера. Эта структура перемещается внутрь парсера при создании и в процессе парсинга флаги оттуда только считываются, но никогда не меняется. Звучит, как идеальный кандидат для вынесения на уровень типов. Я решил провести эксперимент: поменять определение Options с этого: bitflags::bitflags! { pub struct Options: u32 { const ENABLE_TABLES = 1 << 1; const ENABLE_FOOTNOTES = 1 << 2; const ENABLE_STRIKETHROUGH = 1 << 3; const ENABLE_TASKLISTS = 1 << 4; const ENABLE_SMART_PUNCTUATION = 1 << 5; const ENABLE_HEADING_ATTRIBUTES = 1 << 6; } } на это: pub struct Options< const ENABLE_TABLES: bool, const ENABLE_FOOTNOTES: bool, const ENABLE_STRIKETHROUGH: bool, const ENABLE_TASKLISTS: bool, const ENABLE_SMART_PUNCTUATION: bool, const ENABLE_HEADING_ATTRIBUTES: bool, >(()); Там, где в коде был вызов, скажем, options.contains(Options::ENABLE_TABLES), в моём варианте стал вызов options.enable_tables() — метод, который просто возвращает значение параметра ENABLE_TABLES. Имея на руках доказуемо константные значения, компилятор может убрать из кода if-ы и вместе с ними лишний код, что должно снизить объём генерируемого кода для каждого варианта парсинга и повысить производительность за счёт отсутствия ветвлений. Я запустил бенчмарки (кстати, разрабы молодцы, они используют criterion), ожидая увидеть увеличение ПЕРФОРМАНСА. И я действительно его увидел... На бенчмарках, число которых можно было пересчитать по пальцам одной руки. Во всех остальных бенмарках производительность либо не поменялась, либо — что было куда чаще — статистически значимо ухудшилась. 😒 То ли я неверно бенчмаркаю (что вполне может быть, ибо на фоне у меня таки висел VS Code), то ли я чего-то не знаю об оптимизирующих компиляторах. Короче, я разочарован, опечален и сконфужен.

#prog #rust #article Writing Non-Trivial Macros in Rust — полезная статья о том, как поэтапно писать на Rust (относительно) сложные декларативные макросы. Ссылается на The Little Book of Rust Macros, которая сама по себе достойна прочтения.

#prog #rust #article Why is my Rust build so slow? — офигенная статья от Амоса про ускорение компиляции большого проекта на Rust

photo content

> Антон делает то, что я люблю в программировании больше всего — совершенно беcсмысленный, возведённый в абсолют бред, на который потрачено больше времени, чем стоило бы<...> Ах, Вафель, ты был абсолютно прав. Кстати, к него на канале уже 400 человечков! 👀🎉

#prog #rust #моё Допустим, нам нужно сделать на Rust бинарное дерево. Казалось бы, плёвое дело: struct TreeNode<T> { value: T, left: Option<Box<Self>>, right: Option<Box<Self>>, } struct Tree<T> { root: Option<TreeNode<T>>, } Однако тут у нас по аллокации на каждый узел, от чего у нас страдает локальность данных и, как следствие, эффективность кеша (не говоря уже о стоимости аллокаций в рантайме). С другой стороны, у нас есть возможность делать дерево произвольной (насколько хватит оперативки, конечно же) высоты. А можем ли мы, отказавшись от произвольной высоты и задавая её предел наперёд, хранить вложенные узлы напрямую, а не через указатель? Как оказалось, да! Но для начала немного о том, как мы будем задавать высоту. Так как решение на const generics потребует специализации и более продвинутой их обработки и потому абсолютно не реализуемо на стабильной версии, воспользуемся для задания высоты числами Пеано (да, я об этом уже писал): struct Z; struct S<T>(T); Теперь немного подумаем о том, как это отобразить на древовидную структуру. Каждый узел дерева высотой N + 1 включает в себя узлы высотой N. Узел же дерева с высотой 0 не должен включать в себя данные вообще. Этого можно добиться, сопоставив Z тип с полями ненаселённого типа. Кажется, что это изложение довольно легко перекладывается на код: определяем трейт с ассоциированным типом, который уменьшает число на единицу, реализуем его для S<T> с типом T, а для Z с типом never Infallible, параметризуем узел высотой Height и параметризуем вложенные узлы <Height as Decrement>::Output... К сожалению, это не работает: если определить узел таки образом, rustc не понимает, что рекурсия рано или поздно кончается, и жалуется на бесконечную вложенность типа без индирекции. Кажется, нам нужен другой подход. С другой стороны, так уж сильно нам его менять не придётся: вместо того, чтобы отображать каждое число на число на единицу меньше, вы воспользуемся структурной индукцией и будем отображать число непосредственно на тип узла: struct Node<T, Next> { value: T, left: Next, right: Next, } Тут важно, что поля left и right имеют тип Next, а не Option<Next>, иначе мы не сможем сделать узел нулевой высоты ненаселённым типом. Собственно, вот как отображение выглядит для Z: use std::convert::Infallible as Never; trait Project<T> { type Projected; } impl<T> Project<T> for Z { type Projected = Node<T, Never>; } Итого узел нулевой высоты нельзя сконструировать, как мы и хотели. Лишь немногим сложнее выглядит отображение для S<N>: impl<T, N> Project<T> for S<N> where N: Project<T>, { type Projected = Node<T, Option<N::Projected>>; } Так как мы не хотим оперировать узлами напрямую (хотя бы потому, что через них затруднительно наложить ограничение на высоту), сделаем обёртку — собственно параметризованное высотой дерево: struct Tree<T, Height: Project<T>> { repr: Height::Projected, } Теперь реализуем парочку вспомогательных методов и попробуем сделать дерево высоты 2 (=S<S<Z>>): let tree: Tree<i32, S<S<Z>>> = Node { value: 42, left: None, right: Some(Node { value: 42, left: None, right: None }), }.into(); Что ж... Оно работает. И даже нормально печатается поле repr, если добавить #[derive(Debug)] на Node. Попробуем теперь поменять тип дерева на дерево с единичной высотой: let tree: Tree<i32, S<Z>> = Node { ... Компилятор ожидаемо жалуется:
error[E0271]: type mismatch resolving <Z as Project<i32>>::Projected == Node<{integer}, Option<_>>
   --> src/main.rs:181:7
    |
181 |     }.into();
    |       ^^^^ expected enum Option, found enum Infallible
    |
    = note: expected struct Node<{integer}, Option<_>>
               found struct Node<i32, Infallible>

Не слишком внятно, но цели статически ограничить высоту дерева мы успешно достигли. Как всегда, весь код в гисте. И на этот раз даже больше, чем в посте: добавлены методы для поиска по дереву (с допущением, что дерево является двоичным деревом поиска) и немного более приятная печать. Оставайтесь на связи. 🤙

#prog #rust Хозяйке на заметку Как известно, если у типа в Rust есть несколько методов от разных трейтов с одними и теми же именами, то попытка вызвать один из них при помощи синтаксиса метода будет прервана компилятором с жалобой на неоднозначность имён, вынуждая прибегнуть к развёрнутому синтаксису вызова (Trait::method(&value, arg1, arg2)). Менее известен тот факт, что методы самого типа (inherent methods) перекрывают одноимённые методы трейтов, так что если одному имени отвечают метод типа и сколько угодно методов трейтов, то предпочтение всегда отдаётся методу самого типа и не вызывает неоднозначностей. Этим можно воспользоваться, чтобы иметь возможность в необобщённом контексте вызывать методы трейта, не импортируя сам трейт: trait Trait { fn method(&mut self, arg: Arg); } struct Type { ... } impl Trait for Type { fn method(&mut self, arg: Arg) { ... } } impl Type { fn method(&mut self, arg: Arg) { Trait::method(self, arg) } } // где-то в другом месте, где в текущем пространстве имён // есть Type, но нету Trait: let mut value = Type::make(...); value.method(arg);

Ещё из плющек: — теперь *const T указатели можно дерефать в константных контекстах — Теперь правила ансайзинга для дженерик структур немного проще[1] — В copy{,_nonoverlapping} опять включили debug_assert'ы[2] — реализация Clone для RSplit<T, P> больше не требует T: Clone — Трейт Termiation теперь реализов для Result<Infallible, E>, позволяя писать fn main() -> Result<Infallible, ErrorType>, для програм которые не заканчиваются успешно через выход из main — Стабилизировали File::options, замену FileOptions::new — Стабилизировали {Option,Result}::unwrap_unchecked — Стабилизировали многие методы Duration и MaybeUninit как const fn — Компилятор теперь будет пытаться применять стабильные методы прежде, чем не стабильные. Это позволит избежать поломок при добавлении в std методов, пересикающихся по именам с методами из сторонних трейтов. — rustdoc теперь показывает методы из всех Deref реализаций, рекурсивно (а не только из первой) [1]: Новые правила позволяют такое: struct A<T, U: ?Sized + 'static>(T, B<T, U>); struct B<T, U: ?Sized>(T, U); fn main() { let x: A<[u32; 1], [u32; 1]> = A([0; 1], B([0; 1], [0; 1])); // This previously did not work as the last field of `A` also mentions `T`, // as only `U` changes this is now allowed thanks to this feature. let _y: &A<[u32; 1], [u32]> = &x; } TL;DR: если последнее поле это структура, которая ансайзиться, то теперь и наружняя структура тоже ансайзиться. А полные новые правила такие: — the tail field depends on at least one type or const parameter not used in any other field — the target struct can be created from the source by replacing only the parameters only found in the last struct field — the tail field implements Unsize from source to target [2]: Ранее они были отключены из-за того, что их нельзя выполнить в const fn Полный список изменений: RELEASES.md#version-1580-2022-01-13

В свежем Rust 1.58.0 наконец-то можно использовать переменные напрямую при форматировании строк:
let person = get_person();
// ...
println!("Hello, {person}!"); // captures the local `person`

Пока что это работает только с именами, а не произвольными выражениями, но всё равно приятно. Прощайте format!("{name}", name = name);!

#prog #rust

Какой ваш любимый язык программирования и почему это Rust?

#itsec #article A deep dive into an NSO zero-click iMessage exploit: Remote Code Execution Подробная техническая статья о том, как именно работает 0-click exploit, позволяющий скомпрометировать устройство без каких-либо действий со стороны пользователя. Опять комбинация out of bounds {write, read} и парсера изображений вкупе с Тьюринговой трясиной одного экзотического формата изображений.