Разыменование сырых указателей
Создание указателей безопасно, но их разыменование требует unsafe:
// Copyright 2023 Google LLC
// SPDX-License-Identifier: Apache-2.0
fn main() {
let mut x = 10;
let p1: *mut i32 = &raw mut x;
let p2 = p1 as *const i32;
// SAFETY: p1 и p2 созданы взятием сырых указателей на локальную переменную, поэтому
// гарантированно не равны null, выровнены и указывают на один объект,
// выделенный (на стеке).
//
// Объект, на который указывают сырые указатели, живёт всё время выполнения функции, поэтому
// он не освобождается, пока существуют сырые указатели. Через ссылки к нему
// не обращаются, пока существуют сырые указатели, и из других потоков одновременно к нему тоже не обращаются.
unsafe {
dbg!(*p1);
*p1 = 6;
// Изменение может корректно наблюдаться через сырой указатель, как в C.
dbg!(*p2);
}
// НЕКОРРЕКТНО. НЕ ДЕЛАЙТЕ ТАК.
/*
let r: &i32 = unsafe { &*p1 };
dbg!(r);
x = 50;
dbg!(r); // Объект, лежащий в основе ссылки, был изменён. Это UB.
*/
}
Хорошей практикой (и требованием руководства по стилю Rust для Android) является написание комментария к каждому блоку unsafe, объясняющего, как код внутри него удовлетворяет требованиям безопасности выполняемых небезопасных операций.
Для разыменования указателей это означает, что указатели должны быть допустимыми, то есть:
- Указатель не должен быть null.
- Указатель должен быть разыменовываемым (в пределах границ одного выделенного объекта).
- Объект не должен быть освобождён.
- Не должно быть одновременного доступа к одному и тому же месту.
- Если указатель получен приведением ссылки, базовый объект должен быть живым, и ни одна ссылка не может использоваться для доступа к памяти.
В большинстве случаев указатель также должен быть правильно выровнен.
Раздел «UNSOUND» показывает пример распространённого вида ошибки UB: наивное взятие ссылки на результат разыменования сырого указателя обходит знание компилятора о том, на какой объект на самом деле указывает ссылка. Поэтому анализатор заимствований не «замораживает» x, и мы можем изменить его, несмотря на существование ссылки на него. Создание ссылки из указателя требует большой осторожности.