Обновить
16K+
54
Morettom@morett1m

Пишу про Rust, Golang и внезапно — про мозг

55
Рейтинг
51
Подписчики
Отправить сообщение

Что происходит при панике в Rust: от макроса до раскрутки стека

Уровень сложностиСредний
Время на прочтение13 мин
Охват и читатели7.7K

Когда я впервые увидел backtrace паники в Rust, я решил, что это просто аналог исключения. Вызвал panic!, стек раскрутился, деструкторы вызвались, поток умер. Примерно как throw в C++ или raise в Python. Потом я попытался передать панику через FFI-границу, и программа молча упала без backtrace. Потом обнаружил, что catch_unwind перестаёт работать, если в Cargo.toml поменять panic = "unwind" на panic = "abort". Потом выяснил, что двойная паника (когда деструктор паникует во время раскрутки) убивает процесс безусловно, и это не баг, а так задумано.

Оказалось, что за простым panic!("oops") стоит сложная система из сменных runtime-ов, платформозависимой раскрутки стека и кучи граничных случаев...

Читать далее

Как Rust обрабатывает repr и ABI на границе с C: что ломается и почему

Уровень сложностиПростой
Время на прочтение12 мин
Охват и читатели10K

Я пишу FFI-код на Rust уже несколько лет и за это время понял одну неприятную вещь: на FFI-границе всё, что может сломаться, ломается молча. Компилятор не предупреждает, тесты проходят, а данные на C-стороне оказываются просто мусором. Или, что хуже, оказываются почти правильными, первые три поля совпадают, а четвёртое сдвинуто на два байта, и ошибка всплывает через недели.

Большинство этих проблем связано с двумя вещами: как структура лежит в памяти (layout) и как данные передаются при вызове функции (ABI). В чистом Rust-коде об этом можно не думать, компилятор всё решает за вас. Но на границе с C эти детали становятся вашей ответственностью.

Читать далее

Вы неправильно пишете асинхронный Rust: .await там, где его не должно быть

Уровень сложностиПростой
Время на прочтение13 мин
Охват и читатели11K

Самая распространённая ошибка в асинхронном Rust — убеждение, что .await означает «подожди, пока операция завершится». Слово в переводе буквально это и значит, поэтому многие расставляют .await после каждого асинхронного вызова, как точки в конце предложений.

А потом оказывается, что сервер обрабатывает один запрос вместо сотни одновременно, мьютекс намертво виснет с одним-единственным вызовом, отмена в select! теряет половину сообщений, и синхронная версия того же кода работает быстрее. Корень всех этих проблем один: .await не означает «жди». Он означает «дай исполнителю право приостановить меня здесь». И пока вы держите в голове первое значение, асинхронный Rust будет вас наказывать.

В статье рассмотрим что компилятор делает с async fn, зачем нужен Pin, как Tokio решает какую задачу опросить следующей, почему std::sync::Mutex в асинхронном коде иногда срабатывает как мина, и почему даже tokio::sync::Mutex может зависнуть.

Читать далее

Что делает match после того, как вы нажали Compile

Уровень сложностиСредний
Время на прочтение11 мин
Охват и читатели12K

Вы пишете match десятки раз в день. Разбираете Option, матчите варианты enum, ловите диапазоны. Выглядит как switch из Си, только мощнее!

Но задумывались ли вы, что происходит, когда компилятор берёт ваш match с вложенными паттернами, гардами и привязками — и превращает его в машинный код? Там, внутри, лежит целый мир: деревья решений, таблицы переходов, niche-оптимизации, и иногда — один-единственный mov, где вы ожидали десяток сравнений.

Пройдем весь путь от паттерна до ассемблера.

Читать далее

Как Rust реализует трейт-объекты и почему dyn Trait медленнее дженериков

Время на прочтение11 мин
Охват и читатели10K

Спросите разработчика: «Почему dyn Trait медленнее дженериков?», в 9 из 10 случаях услышите: «Потому что косвенный вызов через vtable». Один дополнительный переход по указателю, промах по кешу, вот и вся разница. Звучит убедительно, и кстати процентов на десять правда.

Настоящая цена динамической диспетчеризации не в самом прыжке через vtable, а в том, что этот прыжок прячет от оптимизатора. LLVM видит непрозрачный call по указателю и пасует. Не может встроить тело, не может раскрутить цикл, не может протащить константу через границу вызова. Один косвенный вызов и целый каскад оптимизаций становится невозможным.

Но чтобы понять, почему так происходит, нужно сначала разобраться, как dyn Trait устроен внутри. Что лежит в этом толстяке, как выглядит vtable в памяти, и чем всё это отличается от того, что делает компилятор с дженериками.

Читать далее

Subtyping и variance в Rust: о чём обычно молчат

Уровень сложностиСредний
Время на прочтение12 мин
Охват и читатели7.2K

Привет, Хабр!

Есть вещи в Rust, которые работают незаметно, пока не ломаются, да ломаются они странно... Компилятор указывает на место, где вы ничего плохого не делали, и говорит про «lifetime mismatch» или «mismatched types» без внятного объяснения почему. Или наоборот: вы ожидаете ошибку, потому что передаёте ссылку с явно другим временем жизни, а компилятор молчит и пропускает.

Оба случая объясняются одним механизмом: variance.

Большинство останавливаются на трёх определениях и паре примеров. Пойдём глубже — до алгебры композиции, до того, как компилятор выводит variance через итерацию фиксированной точки, до #[may_dangle] и до того, почему NonNull<T> ковариантен, а *mut T нет.

Читать далее

Rust прячет инструменты там, где вы их не ищете

Уровень сложностиПростой
Время на прочтение10 мин
Охват и читатели11K

Привет, Хабр!

В первой части мы разобрали never-тип !, макрос matches!, std::hint::black_box, прозрачные обёртки с repr(transparent) и transmute_copy. Если не читали — загляните, там фундаментальные штуки. Сегодня продолжаем: ещё шесть возможностей.

Читать далее

Send и Sync в Rust: что решает компилятор за вашей спиной

Уровень сложностиСредний
Время на прочтение10 мин
Охват и читатели9.8K

Привет, Хабр!

Сегодня рассмотрим Send и Sync. Не «что это такое» (это вы в book прочитаете за пять минут), а как именно компилятор принимает решения, почему &mut T внезапно Sync, и что происходит, когда вы пишете unsafe impl Send.

Читать далее

#[inline] в Rust — это не про инлайнинг. И вот почему вы расставляете его не там

Уровень сложностиПростой
Время на прочтение8 мин
Охват и читатели12K

Вы открываете горячую функцию в профилировщике, видите миллионы вызовов, добавляете #[inline(always)]. Бинарник распухает, время сборки подскакивает, а производительность не меняется. Или ваще падает. Проблема не в атрибуте. Проблема в том, что #[inline] делает совсем не то, что подсказывает интуиция.

Читать далее

Почему macro_rules! может не остановиться и что с этим делает Rust

Уровень сложностиСредний
Время на прочтение12 мин
Охват и читатели8.5K

Приветствую.

В 1936 году Алан Тьюринг опубликовал статью, которая среди прочего доказала одну неприятную вещь: невозможно написать алгоритм, который для любой программы и любого входа определит, остановится программа или нет. Проблема остановки. Мы натыкаемся на неё гораздо чаще, чем кажется. Иногда прямо в Cargo.toml.

Читать далее

Нестандартные фичи Rust, которые вы полюбите

Уровень сложностиПростой
Время на прочтение9 мин
Охват и читатели14K

Привет, Хабр! В Rust есть тип, у которого нет ни одного возможного значения. Звучит необычно. Но я однажды столкнулся с этим самым никогда‑типом и понял — без него жить в Rust уже не хочется! Что это такое и зачем нужно — разберём подробно. По ходу дела упомянем и связанные фичи: Infallible, новоявленные макросы вроде matches!, разные фишки для оптимизации кода и FFI, про которые часто не догадываешься.

Читать далее

Miri ловит то, что пропускает компилятор

Уровень сложностиСредний
Время на прочтение13 мин
Охват и читатели6.8K

Привет! Сегодня рассмотрим инструмент, который поможет вам с низкоуровневым кодом на Rust. Если вы пишете на Rust только безопасный код, возможно, никогда о нём не слышали.

А вот тем, кто периодически заглядывает в тёмные уголки unsafe, этот инструмент сэкономит нервишки.

Читать далее

Чатик в лесу: как шимпанзе комбинируют звуки в сложные сообщения

Уровень сложностиПростой
Время на прочтение11 мин
Охват и читатели9.4K

Принято думать, что у людей — сложный язык со словами и правилами, а у животных все примитивно: один крик — одно сообщение («нашёл банан», «вижу хищника» и так далее).

Шимпанзе кричат, фыркают, ухают — но это же просто шум, правда..? Ан нет! Исследование показывает, что наши волосатые родственники комбинируют свои возгласы так, что получаются как бы сложные «слова» и фразы. Причем делают они это разнообразнее, чем считалось раньше.

Читать далее

«Лучшие» практики Rust, которые вас подведут

Уровень сложностиПростой
Время на прочтение13 мин
Охват и читатели15K

Привет, Хабр!

Знаете, что общего между документацией Rust и советами бабушки? И то, и другое звучит разумно, пока не начнёшь применять буквально ко всему. «Используй дженерики для переиспользования кода», «оборачивай общие данные в Arc<Mutex>», «создавай типизированные ошибки» — всё это написано в книгах, статьях и туториалах. И всё это может превратить ваш проект в нечто, от чего хочется плакать.

Читать далее

Осторожно, Drop: как невинный деструктор рушит код

Уровень сложностиПростой
Время на прочтение13 мин
Охват и читатели9.1K

Сегодня мы поговорим про такую, казалось бы, простую и привычную штуку, как функция drop в языке Rust. Что может быть банальнее?

Создали переменную, и в конце области видимости она сама очистится. RAII за нас делает всю работу, а мы просто выдыхаем и не боимся утечек памяти. Но не все так радужно!

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

Читать далее

Почему мандаринка в июле телепортирует тебя в детство

Уровень сложностиПростой
Время на прочтение10 мин
Охват и читатели8.7K

Декабрь. За окном то ли снег, то ли дождь, в магазинах уже вовсю продают елки, а в воздухе витает этот неуловимый коктейль из хвои, мандаринов и чего-то еще, что безошибочно сигнализирует: скоро Новый год.

И вот ты стоишь где-нибудь в очереди, вдыхаешь случайно долетевший аромат цитрусовых, и тебя накрывает. Вот ты маленький, вот бабушкина квартира, вот тот самый телевизор с «Иронией судьбы», вот миска с оливье, к которой тебе еще не разрешают подходить до боя курантов.

Читать далее

Всё, что нужно знать про аллокаторы в Rust (и как написать свой)

Уровень сложностиПростой
Время на прочтение13 мин
Охват и читатели11K

Мне в Rust всегда заходила одна штука. Он довольно быстро приучает не держать в голове мусор из серии «а я точно это освободил??». Большая часть рутины с памятью уезжает в автоматизм языка, и ты можно прям выдохнуть и думать про данные и инварианты, а не про то, где у тебя очередной free потерялся.

Но. Как только начинаешь копать чуть глубже, выясняется, что у Rust есть вполне конкретная рука, которая раздаёт память под все эти Box::new(42), Vec::push и растущие String. Имя этой руке простое: аллокатор. Он отвечает за то, что происходит в куче, и именно через него проходят почти все интересные истории про производительность и поведение памяти.

Читать далее

Как Rust думает о памяти: &mut, provenance и noalias

Уровень сложностиСредний
Время на прочтение12 мин
Охват и читатели9K

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

Сегодня с вами посмотрим изнанки модели памяти Rust. Поговорим о том, кто имеет право на доступ к памяти, что такое provenance указателей, почему &mut T считается эксклюзивным и как все это добралось до уровня оптимизатора LLVM через атрибут noalias.

Читать далее

Что происходит после fn main() в Rust?

Уровень сложностиПростой
Время на прочтение12 мин
Охват и читатели13K

Привет!

Хочу вместе с вами разобрать, как же код на Rust превращается в готовый исполняемый файл. Мы пишем программу, например, fn main() { println!("Hello, Habr!"); }, компилируем, и на выходе получаем бинарник. Что происходит под капотом компилятора Rust в этот момент? Давайте аккуратненько заглянем внутрь этого таинственного процесса.

Читать далее

Нейроны, перестановки и степень двойки — что у них общего?

Уровень сложностиПростой
Время на прочтение10 мин
Охват и читатели12K

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

Казалось бы, при чём здесь мозг?

В нейробиологии давно витает идея, что нейронные сети организованы не случайно, а по неким правилам. Еще канадский психолог Дональд Хебб в 1949 году предположил, что нейронные ансамбли. Грубо говоря, если группа клеток вместе активируется при каком-то событии, она образует узнаваемый шаблон, память или образ. Но вот как именно мозг организует такие группы, оставалось загадкой.

Однако еще 10 лет назад появилась теория о том, что интеллект возникает благодаря удивительно простой математической логике связей между нейронами.

Читать далее
1

Информация

В рейтинге
152-й
Откуда
Aisaroaivve, None, Норвегия
Дата рождения
Зарегистрирован
Активность

Специализация

Backend Developer
Middle
Rust
High-loaded systems
SQL
English
PostgreSQL