% Встреча безопасного и небезопасного

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

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

С учетом сказанного Rust абсолютно безопасный язык программирования.

Ну, вообще-то, в Rust есть безопасный язык программирования. Отступим немного назад.

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

Безопасный Rust это и есть язык программирования Rust. Если вы пишете все на Безопасном Rust, вам никогда не придется волноваться о безопасности типов или памяти. Вы никогда не испытаете проблем с нулевым или висячим указателями или всей этой чушью с неопределенным поведением.

Это офигенно круто.

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

Но возможно вам хочется говорить на другом языке. Может вы пишите низкоуровневые абстракции, не включенные в стандартную библиотеку. Может вы просто пишете стандартную библиотеку (которая написана полностью на Rust). Может вам нужно сделать то, что не понимает система типов, и просто программку из чертовских бит. Может вам нужен Небезопасный Rust.

Небезопасный Rust это тот же Безопасный Rust, с теми же правилами и семантикой. Но он позволяет вам делать некоторые штуки, которые Точно Не Безопасны.

Единственные отличия Небезопасного Rust в том, что вы можете:

  • Разыменовывать сырые указатели
  • Вызывать unsafe функции (включая функции на Си, встроенные функции, сырое распределение)
  • Реализовывать unsafe типажи
  • Изменять статические переменные

Вот и все. Эти операции сделаны Небезопасными, потому что злоупотребление ими вызывает абсолютно Неопределенное Поведение. При вызове неопределенного поведения компилятор может самовольно делать плохие вещи с вашей программой. Вы совершенно точно не должны вызывать Неопределенное поведение!

В отличие от Си, вызвать Неопределенное поведение в Rust могут только несколько операций. Базовый язык заботится о предотвращении следующих действий:

  • Разыменование нулевых или висячих указателей
  • Чтение [неинициализированной памяти]
  • Нарушение [правил совпадения указателей]
  • Создание неверных примитивных значений:
    • висячих/нулевых ссылок
    • bool, который не 0 и не 1
    • неопределенных вариантов enum
    • char вне границ [0x0, 0xD7FF] и [0xE000, 0x10FFFF]
    • Не-utf8 str
  • Размотка в другой язык (unwinding)
  • Вызов гонок данных

Вот и все. Только эти операции вызывают неопределенное поведение в Rust. Конечно, небезопасные функции и типажи могут спокойно сами определять ограничения, которые должны соблюдаться в программе для избежания Неопределенного поведения. Однако смысл их все равно сведется к одной из проблем выше. Некоторые дополнительные ограничения могут появиться благодаря встроенным функциям компилятора, который делает предположения об оптимизации кода.

С другой стороны Rust довольно либерально относится к некоторым сомнительным операциям. Rust считает "безопасными":

  • Дедлоки
  • Состояние гонки
  • Утечка памяти
  • Невызванные деструкторы
  • Переполнение целых чисел
  • Аварийное завершение программы
  • Удаление рабочей базы данных

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