RustMiddleTechnical
Что такое raw pointers в Rust и когда их следует использовать?
Raw pointers (*const T / *mut T) — указатели без гарантий borrow checker; разыменование требует unsafe. Используются в FFI, ручном управлении памятью и нестандартных структурах данных.
Raw pointers в Rust
Raw pointers — это указатели *const T и *mut T, которые существуют вне системы владения и заимствования Rust. Компилятор не гарантирует их корректность: они могут быть нулевыми, висячими или указывать на освобождённую память. Разыменование raw pointer разрешено только внутри блока unsafe.
Отличия от ссылок
- Не подчиняются правилу одного изменяемого или нескольких неизменяемых заимствований.
- Не имеют времени жизни (lifetime), поэтому borrow checker их не отслеживает.
- Могут быть нулевыми —
std::ptr::null()иstd::ptr::null_mut(). - Разыменование через
*ptrтребует блокаunsafe. - Арифметика указателей (
ptr.add(n),ptr.offset(n)) разрешена только вunsafe.
Когда применяются
- FFI: взаимодействие с C-библиотеками через
extern "C". - Реализация нестандартных структур данных (связные списки, арены, интрузивные структуры).
- Оптимизации, где гарантия корректности обеспечивается вручную.
- Работа с
std::mem::MaybeUninitдля неинициализированной памяти.
Пример: FFI и ручное управление памятью
use std::alloc::{alloc, dealloc, Layout};
use std::ptr;
fn main() {
let layout = Layout::new::<u64>();
unsafe {
// Выделяем память вручную
let raw: *mut u64 = alloc(layout) as *mut u64;
if raw.is_null() {
panic!("allocation failed");
}
// Записываем значение через raw pointer
ptr::write(raw, 42u64);
// Читаем значение
let value = ptr::read(raw);
println!("value = {}", value); // value = 42
// Освобождаем вручную
dealloc(raw as *mut u8, layout);
}
}
Пример: взаимодействие с C через FFI
extern "C" {
fn strlen(s: *const u8) -> usize;
}
fn main() {
let s = b"hello\0";
let len = unsafe { strlen(s.as_ptr()) };
println!("len = {}", len); // len = 5
}
Создание raw pointer из ссылки
fn main() {
let mut x = 10i32;
let r: *mut i32 = &mut x as *mut i32;
unsafe {
*r += 5;
}
println!("{}", x); // 15
}
Подводные камни
- Use-after-free: raw pointer не отслеживает время жизни объекта, поэтому легко получить висячий указатель после освобождения памяти.
- Нулевой указатель: разыменование
std::ptr::null()вызывает неопределённое поведение (UB). Всегда проверяйтеptr.is_null(). - Неверное выравнивание:
ptr::read/ptr::writeтребуют правильного выравнивания. Для невыровненного доступа используйтеptr::read_unaligned. - Двойное освобождение: если несколько raw pointer указывают на одну область, освобождение через
deallocдважды — UB. - Гонки данных:
*mut TнеSend/Syncпо умолчанию, но можно обойти это вручную, что открывает гонки данных. - Арифметика за пределами аллокации:
ptr.add(n)за границами выделенного блока — UB, даже если не разыменовывается. - Замена Box::into_raw без Box::from_raw: если вызвать
Box::into_rawи не вернуть владение черезBox::from_raw, будет утечка памяти.
Common mistakes
- Отвечать определением без production-сценария.
- Не называть runtime boundary, security boundary или failure mode.
- Игнорировать версию API, observability и тестовую проверку.
What the interviewer is testing
- Объясняет механизм своими словами и без выдуманных API.
- Называет реальные риски, диагностику и критерий корректности.
- Связывает ответ с текущей документацией и миграционными ограничениями.