Розіменування "сирих" вказівників
Створення вказівників є безпечним, але для їх розіменування потрібно 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); */ }
This slide should take about 10 minutes.
Хорошою практикою є (і вимагається посібником зі стилю Android Rust) писати коментар до кожного unsafe
блоку, пояснюючи, наскільки код у ньому відповідає вимогам безпеки небезпечних операцій, які він виконує.
У випадку розіменувань покажчиків це означає, що покажчики мають бути дійсними, тобто:
- Покажчик має бути ненульовим.
- Покажчик має бути розіменоваючим (у межах одного виділеного об’єкта).
- Об’єкт не повинен бути звільнений.
- Не повинно бути одночасних доступів до того самого розташування.
- Якщо вказівник було отримано шляхом приведення посилання, базовий об’єкт має бути дійсним, і жодне посилання не може використовуватися для доступу до пам’яті.
У більшості випадків вказівник також має бути правильно вирівняний.
У розділі "НЕ БЕЗПЕЧНО" наведено приклад поширеної помилки UB: *r1
має 'static
час життя, тому r3
має тип &'static String
, і таким чином переживає s
. Створення посилання з покажчика вимагає великої обережності.