Небезпечні операції
Як вступ до цього розділу, запозичуючи з офіційної документації,
“слід намагатися мінімізувати кількість unsafe-коду в кодовій базі.” З цим
знанням, почнімо! Позначення unsafe у Rust використовуються для обходу
захистів, які запроваджує компілятор; зокрема, є чотири основні
речі, для яких використовується unsafe:
- розіменування сирих вказівників
- виклик функцій або методів, які є
unsafe(зокрема, виклик функції через FFI, див. попередній розділ книги) - доступ до статичних змінних, що можуть змінюватися, або їх змінення
- реалізація небезпечних трейтів
Сирі вказівники
Сирі вказівники * і посилання &T функціонують подібно, але посилання
завжди безпечні, тому що гарантовано вказують на дійсні дані завдяки
перевірнику запозичень. Розіменування сирого вказівника можна виконати лише через unsafe-блок.
fn main() {
let raw_p: *const u32 = &10;
unsafe {
assert!(*raw_p == 10);
}
}
Виклик unsafe-функцій
Деякі функції можуть бути оголошені як 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 одна з припущень, які мають бути дотримані, —
це те, що переданий вказівник вказує на дійсну пам’ять і що пам’ять, на яку
він вказує, має правильний тип. Якщо ці інваріанти не дотримані, то поведінка
програми є невизначеною, і неможливо знати, що станеться.