Небезопасные операции

В качестве введения в этот раздел процитируем официальную документацию, "нужно стараться минимизировать количество небезопасного кода в кодовой базе." Имея это в виду, давайте начнём! Небезопасные аннотации в Rust используются для обхода блокировок защиты, устанавливаемых компилятором; в частности, существует четыре основных варианта использования небезопасного кода:

  • разыменование сырых указателей
  • вызов функций или методов, которые являются unsafe (включая вызов функции через FFI см. предыдущую главу книги)
  • доступ или изменение статических изменяемых переменных
  • реализация небезопасных типажей

Сырые указатели

Сырые указатели * и ссылки &T имеют схожую функциональность, но ссылки всегда безопасны, потому что они гарантированно указывают на достоверные данные за счёт механизма проверки заимствований. Разыменование же сырого указателя можно выполнить только через небезопасный блок.

fn main() {
    let raw_p: *const u32 = &10;

    unsafe {
        assert!(*raw_p == 10);
    }
}

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

Некоторые функции могут быть объявлены как unsafe, то есть за корректность этого кода несёт ответственность программист, а не компилятор. Пример - это метод std::slice::from_raw_parts, который создаст срез из указателя на первый элемент и длины.

use std::slice;

fn main() {
    let some_vector = vec![1, 2, 3, 4];

    let pointer = some_vector.as_ptr();
    let length = some_vector.len();

    unsafe {
        let my_slice: &[u32] = slice::from_raw_parts(pointer, length);

        assert_eq!(some_vector.as_slice(), my_slice);
    }
}

Для slice::from_raw_parts одним из обязательств, которое должно быть выполнено, является факт того, что переданный указатель указывает на допустимую память, и что в памяти лежит значение правильного типа. Если эти условия не выполнены, то поведение программы не определено, и неизвестно, что произойдёт.