1. 1. Introduction
  2. 2. Введение
  3. 3. C чего начать
  4. 4. Изучение Rust
    1. 4.1. Угадайка
    2. 4.2. Обедающие философы
    3. 4.3. Вызов кода на Rust из других языков
  5. 5. Синтаксис и семантика
    1. 5.1. Связывание имён
    2. 5.2. Функции
    3. 5.3. Простые типы
    4. 5.4. Комментарии
    5. 5.5. Конструкция `if`
    6. 5.6. Циклы
    7. 5.7. Владение
    8. 5.8. Ссылки и заимствование
    9. 5.9. Время жизни
    10. 5.10. Изменяемость
    11. 5.11. Структуры
    12. 5.12. Перечисления
    13. 5.13. Конструкция `match`
    14. 5.14. Шаблоны сопоставления `match`
    15. 5.15. Синтаксис методов
    16. 5.16. Вектора
    17. 5.17. Строки
    18. 5.18. Обобщённое программирование
    19. 5.19. Типажи
    20. 5.20. Типаж `Drop`
    21. 5.21. Конструкция `if let`
    22. 5.22. Типажи-объекты
    23. 5.23. Замыкания
    24. 5.24. Универсальный синтаксис вызова функций
    25. 5.25. Контейнеры и модули
    26. 5.26. `const` и `static`
    27. 5.27. Атрибуты
    28. 5.28. Псевдонимы типов
    29. 5.29. Приведение типов
    30. 5.30. Ассоциированные типы
    31. 5.31. Безразмерные типы
    32. 5.32. Перегрузка операций
    33. 5.33. Преобразования при разыменовании
    34. 5.34. Макросы
    35. 5.35. Сырые указатели
    36. 5.36. Небезопасный код
  6. 6. Эффективное использование Rust
    1. 6.1. Стек и куча
    2. 6.2. Тестирование
    3. 6.3. Условная компиляция
    4. 6.4. Документация
    5. 6.5. Итераторы
    6. 6.6. Многозадачность
    7. 6.7. Обработка ошибок
    8. 6.8. Выбор гарантий
    9. 6.9. Интерфейс внешних функций
    10. 6.10. Типажи `Borrow` и `AsRef`
    11. 6.11. Каналы сборок
    12. 6.12. Using Rust without the standard library
  7. 7. Нестабильные возможности Rust
    1. 7.1. Плагины к компилятору
    2. 7.2. Встроенный ассемблерный код
    3. 7.3. Без stdlib
    4. 7.4. Внутренние средства
    5. 7.5. Элементы языка
    6. 7.6. Продвинутое руководство по компоновке
    7. 7.7. Тесты производительности
    8. 7.8. Синтаксис упаковки и шаблоны `match`
    9. 7.9. Шаблоны `match` для срезов
    10. 7.10. Ассоциированные константы
    11. 7.11. Пользовательские менеджеры памяти
  8. 8. Глоссарий
  9. 9. Syntax Index
  10. 10. Библиография

Небезопасный код

Главная сила Rust — в мощных статических гарантиях правильности поведения программы во время исполнения. Но проверки безопасности очень осторожны: на самом деле, существуют безопасные программы, правильность которых компилятор доказать не в силах. Чтобы писать такие программы, нужен способ немного ослабить ограничения. Для этого в Rust есть ключевое слово unsafe. Код, использующий unsafe, ограничен меньше, чем обычный код.

Давайте рассмотрим синтаксис, а затем поговорим о семантике. unsafe используется в четырёх контекстах. Первый — это объявление того, что функция небезопасна:

unsafe fn beregis_avtomobilya() {
    // страшные вещи
}Run

Например, все функции, вызываемые через FFI, должны быть помечены как небезопасные. Другое использование unsafe — это отметка небезопасного блока:

unsafe {
    // страшные вещи
}Run

Третье — небезопасные типажи:

unsafe trait Scary { }Run

И четвёртое — реализация (impl) таких типажей:

unsafe impl Scary for i32 {}Run

Важно явно выделить код, ошибки в котором могут вызвать большие проблемы. Если программа на Rust падает с "segmentation fault", можете быть уверены — проблема в участке, помеченном как небезопасный.

Что значит "безопасный"?

В контексте Rust "безопасный" значит "не делает ничего небезопасного". Также важно знать, что некоторое поведение скорее всего нежелательно, но явно не считается небезопасным:

Rust не может предотвратить все виды проблем в программах. Код с ошибками может и будет написан на Rust. Вышеперечисленные вещи неприятны, но они не считаются именно что небезопасными.

В дополнение к этому, ниже представлен список неопределённого поведения (undefined behavior) в Rust. Избегайте этих вещей, даже когда пишете небезопасный код:

Сверхспособности небезопасного кода

В небезопасном блоке или функции, Rust разрешает три ситуации, которые обычно запрещены. Всего три. Вот они:

  1. Доступ к или изменение статической изменяемой переменной.
  2. Разыменование сырого указателя.
  3. Вызов небезопасных функций. Это самая мощная возможность.

Это всё. Важно отметить, что unsafe, например, не "выключает проверку заимствования". Объявление какого-то кода небезопасным не изменяет его семантику; небезопасность не означает принятие компилятором любого кода. Но она позволяет писать вещи, которые нарушают некоторые из правил.

Вы также встретите ключевое слово unsafe, когда будете реализовывать интерфейс к чужому коду не на Rust. Идиоматичным считается написание безопасных обёрток вокруг небезопасных библиотек.

Давайте поговорим о трёх упомянутых возможностях, доступных в небезопасном коде.

Доступ или изменение static mut

Rust позволяет пользоваться глобальным изменяемым состоянием с помощью static mut. Это может вызвать гонку по данным, и в сущности небезопасно. Подробнее смотрите раздел о static.

Разыменование сырого указателя

Сырые указатели поддерживают произвольную арифметику указателей, и могут вызвать целый ряд проблем безопасности памяти и безопасности в целом. В каком-то смысле, возможность разыменовать произвольный указатель — одна из самых опасных вещей, которые вы можете сделать. Подробнее смотрите раздел о сырых указателях.

Вызов небезопасных функций

Эта возможность затрагивает то, откуда можно делать вызов небезопасного кода: небезопасные функции могут вызываться только из небезопасных блоков.

Мощь и полезность этой возможности сложно переоценить. Rust предоставляет некоторые intrinsic-операции компилятора в виде небезопасных функций, а некоторые небезопасные функции обходят проверки безопасности для достижения большей скорости исполнения.

В заключение, повторимся: хотя вы и можете делать в небезопасных участках почти что угодно, это не значит, что стоит это делать. Компилятор будет предполагать выполнение оговоренных инвариантов, так что будьте осторожны!