Комментарии 27
программисты C++ должны вручную управлять памятьюВот сейчас обидно было. Не должны, более того, «должны не вручную», в большинстве случаев, кроме тех, когда это действительно необходимо.
Через примитивы синхронизации.
Я вас удивлю, но в С++ запись в переменную без примитивов синхронизации — UB по Стандарту. И если Rust ругается во время компиляции, то C++ делает rm -rf
.
Я вас удивлю, но в С++ запись в переменную без примитивов синхронизации — UB по Стандарту.
Не удивили капитан.
А я думал что вообще в принципе нельзя писать, то есть если обернуть мьютексом или если переменная атомик то можно использовать? Или там другая концепция синхронизации?
Да, именно так. В систему типов компилятора встроено, что какой типаж можно шарить, а какой нет. И пользовательский код может объявлять свои типы, реализовывать типаж "ты можешь быть разделен между потоками", так что это не прибито гвоздями.
Если интересует больше, можете почитать https://doc.rust-lang.org/book/ch16-04-extensible-concurrency-sync-and-send.html?highlight=sync,send#extensible-concurrency-with-the--sync--and--send--traits
Нет.
Я подозреваю, что все UB, присущие C++.
Дедлок — это отдельный разговор, дедлок в Rust считают ошибкой логики программы, а не корректности кода. То есть при наступлении дедлока максимум что случится в Rust программе — все повиснет. Но при этом вы можете быть уверены, что не будет UB, не будет веселых побочных эффектов в виде порчи данных.
Для переменной, в которой находится мьютекс, действуют такие же правила что и для любой другой.
Может. Этот подход не зависит от того, какой тип у переменной.
Функции mem::zeroed()
и mem::unitialized
являются unsafe
, так что раст тут ничего статически проверять и не будет, если нарушите инварианты — ССБЗ. Из цитаты разработчика кортимы:
let x: bool = mem::uninitialized();
This is UB. It has nothing to do with references.
On all currently-supported platformsbool
has only two valid values,true
(bit-pattern:0x1
) andfalse
(bit-pattern:0x0
).
mem::uninitialized
, however, produces a bit-pattern where all bits have the valueuninitialized
. This bit-pattern is neither0x0
nor0x1
, therefore, the resultingbool
is invalid, and the behavior is undefined.
А вообще в расте не принято создавать неинициализированную память, да и не нужно в общем-то. Почти все юзкейсы это "надо дать буфер для FFI".
А шаринг неинициализированной памяти между потоками чревато плохими последствиями в любом языке. Поэтому не могу понять реальный практический смысл этой задачи.
Есть «болванка», в одном потоке. Инициализация ее идёт в другом потоке. Но третий поток может обратиться к переменной раньше чем она будет проинициализированна во втором потоке.
Так же. Будут проверены условия Sync и Send и компилятор запретит вам такую чудесную "оптимизацию".
Из первого же примера непонятно, каким образом s1 и s2 выходят за пределы области видимости
Это не совсем область видимости. В строке s2=s1 происходит не копирование, а перемещение. Теперь s1 как бы пустая, и компилятор не дает ей пользоваться. С другой стороны, мы можем присвоить ей какую-нибудь новую строчку, и тогда сможем с ней снова работать.
Теперь s1 как бы пустая
Не просто пустая, а s1 на самом деле уничтожается в пользу s2.
В Rust перемещение отличается от перемещения в C++. Если в C++ std::move тесно связан с конструкторами перемещения и оператором присваивания перемещением, то в Rust код вида a = b
— это перемещение, если b не является Copy (Copy — тип, который можно скопировать бит за битом: числа, bool...) типом.
Таким образом, если в C++ вы делаете std::move(s1)
, то лексически время жизни s1 не завершилось, у неё некое неспецифицированное значение, работа с которым может привести к неопределенному поведению.
Тогда как в Rust перемещение ( s2=s1
) лексически уничтожает переменную s1. И теперь s1 — это не "пустая строка", это строка, которой нет.
Бесстрашная защита. Безопасность памяти в Rust