MaybeUninit для ручной сборки структур без лишнего drop
Когда значение собирается по частям в unsafe-коде, FFI, парсере или аллокаторе, главная проблема не в самой инициализации. Главная проблема - что делать, если ошибка произошла посередине.
Например, одно поле структуры уже создано, а второе еще нет. В этот момент нельзя дропать всю структуру целиком: для Rust она еще не является полностью готовым объектом.
Правильный подход - явно помнить, какие поля уже инициализированы, и очищать только их. Для этого используют MaybeUninit, ptr::write, drop_in_place и, когда значение уже полностью готово, assume_init_drop.
MaybeUninit нужен не “чтобы обмануть Rust”, а чтобы аккуратно управлять жизненным циклом там, где компилятор уже не может сделать это за тебя.
Для таких случаев есть MaybeUninit:
use std::mem::MaybeUninit;
struct Buffer {
data: Vec<u8>,
meta: String,
}
let mut value = MaybeUninit::<Buffer>::uninit();
unsafe {
let ptr = value.as_mut_ptr();
std::ptr::addr_of_mut!((*ptr).data).write(vec![1, 2, 3]);
// Если дальше случилась ошибка,
// дропаем только уже инициализированное поле
std::ptr::addr_of_mut!((*ptr).data).drop_in_place();
// А всю Buffer не трогаем:
// meta еще не была создана
}
Более удобный вариант для полностью инициализированного MaybeUninit<T>:
let mut value = MaybeUninit::new(String::from("hello"));
unsafe {
value.assume_init_drop();
}
MaybeUninit<T> используют не просто для создания значения “без инициализации”. Он нужен, когда ты сам управляешь моментом, в который память становится настоящим T, и сам отвечаешь за корректный drop.
Если структура собирается по частям, компилятор уже не всегда может понять, какие поля реально созданы. Поэтому unsafe-логику лучше держать внутри маленькой функции или типа, а наружу отдавать обычный safe API. Так риск ошибок остается локальным и проверяемым.
#rust #rustlang #programming #unsafeRust
Please open Telegram to view this post
VIEW IN TELEGRAM
❤9🔥4🥰3🥴2👍1🤗1