Comments 9
Отличная статья. Новички в Ржавчине часто не понимают какова роль unsafe в языке и или боятся его как огня и пытаются избегать любыми путями, или наоборот неоправданно активно его используют. В обоих крайностях качество кода сильно страдает — или вместо одного аккуратного сырого указателя получаются монструозные и неуклюжие нагромождения безопасных абстракций, или весь код представляет из себя тотальное нагромождение UB. Даешь больше разъяснительных статей! :)
А что думаете по поводу недавнего поста
(https://manishearth.github.io/blog/2017/12/24/undefined-vs-unsafe-in-rust/?utm_source=newsletter_mailer&utm_medium=email&utm_campaign=weekly)?
… The reason it is still unsafe is because it’s possible to trigger UB by only changing the “safe” caller code. I.e. “changes to code outside unsafe blocks can trigger UB if they include calls to this function”.
Плюс список того, что считается UB (https://doc.rust-lang.org/beta/reference/behavior-considered-undefined.html).
Проблемы, как я понимаю, могу возникнуть в случае использования какого-нибудь чужого крейта. Так например у Tock https://www.tockos.org/blog/2017/crates-are-not-safe/ возникают сложности, т.к. вынуждает их вручную контроллировать зависимости и их изменения.
Статья "Undefined vs Unsafe in Rust" — тоже очень хорошая.
Вопросы с появлением UB при косвенном использовании небезопасного кода должны решаться путем его полного упрятывания в модуль, формирующий цельную и безопасную абстракцию, обеспечивающую все нужныне для правильной работы unsafe-кусков инварианты.
Иронично, что оригиналу этой статьи полтора года, а неделю назад вышел пост в другом блоге с тем же названием.
unsafe — это как сказать компилятору и всем коллегам: вот здесь я делаю страшные вещи, я уверен, что я делаю то, что нужно, а если чё-то вдруг за сегфолтилось, то ищите причину здесь, больше негде.
Да. Стоит заметить, что, например, в коде самого Rust компилятора(!), только 4% кода является unsafe
, да и то unsafe
там используется в основном для взаимодействия с C библиотеками посредством FFI(Foreign Function Interface).
Поэтому в подавляющем большинстве случаев, прирост производительности происходит за счет опускания неких проверок, обязательных для обеспечения безопасности в общем случае.
Например, если у вас есть вектор элементов то, как уже было сказано в статье, безопасная реализация обязана проверить, что переданный индекс не выходит за пределы массива. Даже если вы храните этот индекс в объекте и твердо уверены, что он верный, при каждом обращении все равно будет выполняться проверка.
В этом случае вы можете использовать небезопасный вариант операции «получить значение по индексу», который не выполняет проверку условия. Тогда ответственность будет лежать на вас.
Обычно такие приемы не делают в пользовательском коде, наоборот, необоснованное трюкачество считается признаком дурного тона. В то же время, в контролируемых условиях, например в стандартной библиотеке, такое может быть полезно, ибо позволяет сделать код более производительным.
Один из примеров, где такое срезание углов имеет смысл — это реализация итератора по массиву. Доступный размер массива известен заранее, поэтому итератору достаточно хранить у себя текущий индекс. При создании итератора текущий индекс выставляется в 0 и увеличивается на единицу каждый раз, когда пользовательский код зовет `next` и если в массиве есть еще непосещенные элементы. Поскольку этот индекс по определению не может выйти за пределы массива, имеет смысл операцию обращения к элементу делать без проверки индекса (проверка заложена в саму логику итератора), тем самым, повышая производительность, путем убирания лишней проверки, а не всех проверок вообще. Код по-прежнему остается безопасным, просто обеспечение этой безопасности вынесено на другой уровень.
По этой самой причине Rust рекомендует писать код именно с использованием итераторов и фильтров, так как они оказываются более эффективными, нежели традиционный императивный код с доступом по индексу.
Конкретные цифры будут слишком малоинформативны, поскольку зависят от сотни факторов, включая конкретное место использования итератора, оценку компилятором весов при инлайнинге, архитектуру процессора и т.д.
Rust: «Небезопасные абстракции»