Изучение небезопасных операций в Rust

Beginner

This tutorial is from open-source community. Access the source code

Введение

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

Примечание: Если лаб не уточняет имя файла, вы можете использовать любое имя файла, которое хотите. Например, вы можете использовать main.rs, скомпилировать и запустить его с помощью rustc main.rs &&./main.

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

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

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

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

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

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

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

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

Некоторые функции могут быть объявлены как небезопасные, что означает, что ответственность за обеспечение правильности ложится на программиста, а не на компилятор. Одним примером является [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 одно из предположений, которое должно быть соблюдено, заключается в том, что указатель, переданный в функцию, указывает на валидную память и что память, на которую он указывает, имеет правильный тип. Если эти инварианты не соблюдаются, то поведение программы не определено и невозможно预知ить, что произойдет.

Резюме

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