ПрограммированиеСистемный программный инженер / Rust Senior Developer

Что такое Unsafe Rust, зачем он нужен, каковы его правила и как правильно использовать unsafe-блоки, чтобы минимизировать риски?

Проходите собеседования с ИИ помощником Hintsage

Ответ

Unsafe Rust — это расширение безопасного подмножества Rust, позволяющее использовать операции, которые компилятор не может проверить на корректность владения, сроков жизни и алиасинга. Основные области применения: взаимодействие с низкоуровневыми библиотеками, FFI, ручной контроль памяти, реализация абстракций, не укладывающихся в модель безопасного Rust.

Ключевые особенности unsafe:

  • Для использования требуется явно объявить блок: unsafe { ... } или функцию: unsafe fn some_func()
  • В unsafe-блоке разрешены небезопасные операции: разыменование raw pointer, вызов небезопасных функций и методов, доступ к union, статическим мутабельным переменным, реализация методов неструктурированной памяти
  • Использование unsafe не делает весь код в блоке опасным для всего языка — только обязуется вручную гарантировать correctness

Пример:

let x: i32 = 10; let ptr: *const i32 = &x as *const i32; unsafe { println!("Value: {}", *ptr); // разыменование raw pointer }

Вопрос с подвохом

Является ли код внутри unsafe-блока полностью небезопасным и не чекан компилятором, или компилятор по-прежнему применяет правила borrow checker и другие проверки?

Ответ: Нет, внутри unsafe-блока компилятор продолжает проверять множество правил Rust (например, типизация, правила владения, синтаксис), но позволяет выполнять только те действия, которые иначе бы не допустил. Нельзя полностью отключить borrow checker!

Пример:

let mut x = 0; let r1 = &mut x as *mut i32; // Запрещено: let r2 = &mut x as *mut i32; // даже внутри unsafe появятся ошибки, если нарушается мутабельность

Примеры реальных ошибок из-за незнания тонкостей темы


История

В асинхронной файловой библиотеке был использован raw pointer для контроля буфера, но забыли корректно отслеживать срок жизни буфера. В результате, при закрытии файла, pointer стал "висячим" и access приводил к undefined behavior (секция unsafe не спасла от use-after-free).


История

В собственной реализации Vec-like структуры программист вручную расширял буфер через unsafe. Ошибка смещения привела к некорректному копированию элементов и data corruption, потому что не были учтены alignment и layout типов.


История

В дескрипторе потоков использовался статический мутабельный указатель (static mut) без должной синхронизации. Из-за этого в многопоточном режиме случайно возникло data race, приводящее к sporadic crash приложения, отлавливаемому только fuzzing.