Розіменування “сирих” вказівників
Створення вказівників є безпечним, але для їх розіменування потрібно unsafe:
fn main() {
let mut s = String::from("обережно!");
let r1 = &raw mut s;
let r2 = r1 as *const String;
// БЕЗПЕКА: r1 та r2 були отримані з посилань і тому
// гарантовано є ненульовими та правильно вирівняними, об'єкти, що лежать в основі
// посилань, з яких вони були отримані, є дійсними на протязі
// всього небезпечного блоку, і до них немає доступу ні через
// посилання, ні одночасно через будь-які інші покажчики.
unsafe {
println!("r1 є: {}", *r1);
*r1 = String::from("ууухооох");
println!("r2 є: {}", *r2);
}
// НЕБЕЗПЕЧНО. НЕ РОБІТЬ ЦЬОГО.
/*
let r3: &String = unsafe { &*r1 };
drop(s);
println!("r3 is: {}", *r3);
*/
}
Хорошою практикою є (і вимагається посібником зі стилю Android Rust) писати коментар до кожного unsafe блоку, пояснюючи, наскільки код у ньому відповідає вимогам безпеки небезпечних операцій, які він виконує.
У випадку розіменувань покажчиків це означає, що покажчики мають бути дійсними, тобто:
- Покажчик має бути ненульовим.
- Покажчик має бути розіменоваючим (у межах одного виділеного об’єкта).
- Об’єкт не повинен бути звільнений.
- Не повинно бути одночасних доступів до того самого розташування.
- Якщо вказівник було отримано шляхом приведення посилання, базовий об’єкт має бути дійсним, і жодне посилання не може використовуватися для доступу до пам’яті.
У більшості випадків вказівник також має бути правильно вирівняний.
У розділі “НЕ БЕЗПЕЧНО” наведено приклад поширеної помилки UB: *r1 має 'static час життя, тому r3 має тип &'static String, і таким чином переживає s. Створення посилання з покажчика вимагає великої обережності.