# Unsafe Rust

> Подмножество Rust, в котором программист берёт на себя гарантии, обычно проверяемые компилятором, — разыменование сырых указателей, вызов FFI, unsafe-функции и трейты; основа безопасных абстракций стандартной библиотеки.

Источник: https://llmdocs.ru/wiki/unsafe-rust/ · обновлено 2026-11-09 · лицензия CC BY-SA 4.0

- **Язык**: Rust
- **Ключевое слово**: unsafe (блоки, функции, трейты, impl, extern-блоки)
- **Пять «суперспособностей»**: сырые указатели, unsafe-функции, изменяемые static, unsafe-трейты, поля union
- **Что не отключается**: проверка заимствований, типов, времён жизни для обычных ссылок
- **Инструменты проверки**: Miri, санитайзеры, cargo-geiger
- **Справочник**: The Rustonomicon
**Unsafe Rust** — часть языка [Rust](https://llmdocs.ru/wiki/rust.md), в которой программист обещает компилятору соблюдать инварианты, которые тот не может проверить сам. Обычный («безопасный») Rust гарантирует отсутствие висячих указателей, гонок данных и выхода за границы через систему [владения и заимствования](https://llmdocs.ru/wiki/ownership-borrowing.md). Но некоторые вещи — работа с железом, вызов библиотек на C, реализация эффективных структур данных — принципиально требуют операций, корректность которых компилятор доказать не может. Для них есть ключевое слово `unsafe`.[1]

## Что разрешает unsafe

`unsafe` не выключает проверки Rust: заимствования, типы и времена жизни обычных ссылок проверяются как всегда. Он лишь открывает пять дополнительных возможностей:[1]

1. разыменовывать **сырые указатели** `*const T` и `*mut T`;
2. вызывать **unsafe-функции**, включая функции из других языков (FFI);
3. читать и изменять **изменяемые статические** переменные (`static mut`);
4. реализовывать **unsafe-трейты** (например, `Send` и `Sync` вручную);
5. обращаться к полям `union`.

```rust
let mut x = 10;
let p = &mut x as *mut i32;   // создать сырой указатель можно и в safe-коде
unsafe {
    *p += 1;                  // а разыменовать — только в unsafe
}
assert_eq!(x, 11);
```

## Неопределённое поведение

Если внутри `unsafe` нарушить правила языка, возникает **неопределённое поведение** (UB): компилятор вправе оптимизировать код, исходя из того, что нарушения не бывает, и программа может вести себя как угодно — не только «упасть». К UB относятся:[3]

- разыменование висячего, нулевого или невыровненного указателя;
- гонка данных;
- одновременное существование `&mut T` и другой ссылки на те же данные;
- создание недопустимого значения: `bool`, отличного от 0 и 1, неинициализированной памяти как целого числа, невалидного UTF-8 в `str`;
- вызов функции с неверным ABI.

Важная мысль Rustonomicon: ошибка может быть *в безопасном коде рядом*, если он нарушает инвариант, на который опирается `unsafe`-блок.[2] Поэтому границей «опасной зоны» на практике считается модуль, а не блок.

## Безопасные абстракции

Главный приём — инкапсулировать `unsafe` внутри функции или типа с **безопасным интерфейсом**, который невозможно использовать неправильно. Вся стандартная библиотека устроена так: `Vec`, `String`, `HashMap`, `Rc`, `Mutex` внутри используют сырые указатели и ручное управление памятью, но снаружи безопасны.

```rust
pub fn split_at_mut<T>(s: &mut [T], mid: usize) -> (&mut [T], &mut [T]) {
    let len = s.len();
    let ptr = s.as_mut_ptr();
    assert!(mid <= len);            // проверка делает unsafe-код корректным
    // SAFETY: части не пересекаются, обе лежат внутри исходного среза.
    unsafe {
        (
            std::slice::from_raw_parts_mut(ptr, mid),
            std::slice::from_raw_parts_mut(ptr.add(mid), len - mid),
        )
    }
}
```

Принятые соглашения:[7]

- каждый `unsafe`-блок сопровождается комментарием `// SAFETY:` с объяснением, почему он корректен;
- у каждой `unsafe fn` в документации есть раздел `# Safety` с условиями, которые должен выполнить вызывающий;
- линт Clippy `undocumented_unsafe_blocks` следит за этим (см. [Clippy и rustfmt](https://llmdocs.ru/wiki/clippy-rustfmt.md)).

В редакции Rust 2024 тело `unsafe fn` больше не считается одним большим unsafe-блоком: опасные операции внутри неё тоже нужно оборачивать в `unsafe { }`, а блоки `extern` пишутся как `unsafe extern "C"`.[5]

## FFI

Вызов функций из библиотек на C — самая частая причина использовать `unsafe`:

```rust
unsafe extern "C" {
    fn strlen(s: *const std::ffi::c_char) -> usize;
}

let s = std::ffi::CString::new("привет").unwrap();
let n = unsafe { strlen(s.as_ptr()) };   // 12 байт в UTF-8
```

Компилятор не знает, что делает чужой код, поэтому ответственность за корректные аргументы лежит на программисте. Обёртки генерируют инструменты `bindgen` (из заголовков C) и `cxx` (для C++), а для экспорта Rust в C — `cbindgen`. Через тот же механизм Rust встраивается в [Python](https://llmdocs.ru/wiki/python.md) (PyO3) и компилируется в [WebAssembly](https://llmdocs.ru/wiki/webassembly.md).

## Сколько unsafe в реальном коде

Исследование 2020 года по крейтам crates.io показало, что около 76 % крейтов не содержат `unsafe` вовсе, а в остальных он сосредоточен в небольших участках — чаще всего для FFI и оптимизаций.[6] Посчитать `unsafe` в зависимостях проекта помогает `cargo-geiger`.

## Как проверять

- **Miri** — интерпретатор промежуточного представления Rust, который во время выполнения тестов обнаруживает многие виды UB: выход за границы, использование после освобождения, нарушения правил алиасинга:[4]

```bash
rustup +nightly component add miri
cargo +nightly miri test
```

- **Санитайзеры** (AddressSanitizer, ThreadSanitizer) через флаги nightly-компилятора.
- **Фаззинг** (`cargo fuzz`) для кода, разбирающего внешние данные.
- Ревью: `unsafe`-код проверяют особенно тщательно, многие проекты запрещают его атрибутом `#![forbid(unsafe_code)]`.

## Unsafe и безопасность памяти

Наличие `unsafe` не отменяет преимуществ Rust перед C и C++: потенциально опасный код явно помечен, локализован и поддаётся аудиту, а остальная программа проверяется компилятором. Это противоположность языкам с ручным управлением памятью, где «опасна» любая строка (см. [Сборка мусора vs ручное управление памятью](https://llmdocs.ru/wiki/gc-vs-manual-memory.md)).

## См. также

- [Владение и заимствование (Rust)](https://llmdocs.ru/wiki/ownership-borrowing.md)
- [Макросы Rust](https://llmdocs.ru/wiki/rust-macros.md)
- [Rust (язык)](https://llmdocs.ru/wiki/rust.md)
- [Go vs Rust — сравнение](https://llmdocs.ru/wiki/go-vs-rust.md)

## Источники

1. [The Rust Programming Language — Unsafe Rust](https://doc.rust-lang.org/book/ch20-01-unsafe-rust.html)
2. [The Rustonomicon](https://doc.rust-lang.org/nomicon/)
3. [The Rust Reference — Behavior considered undefined](https://doc.rust-lang.org/reference/behavior-considered-undefined.html)
4. [Miri — an interpreter for Rust's mid-level IR](https://github.com/rust-lang/miri)
5. [Rust 2024 Edition Guide — Unsafe extern blocks and unsafe_op_in_unsafe_fn](https://doc.rust-lang.org/edition-guide/rust-2024/unsafe-op-in-unsafe-fn.html)
6. [Astrauskas et al. — How do programmers use unsafe Rust? (OOPSLA 2020)](https://doi.org/10.1145/3428204)
7. [Rust API Guidelines — Documentation (Safety sections)](https://rust-lang.github.io/api-guidelines/documentation.html)
