История вопроса:
Rust был изначально задуман как язык, приоритетом которого является безопасность памяти. Однако в некоторых задачах — например, при работе с FFI или низкоуровневыми аллокаторами — приходится использовать raw pointers и управлять динамической памятью вручную. Такие задачи встречаются и в системном программировании, и в оптимизации производительности. Поэтому важно знать, как Rust предотвращает утечки, dangling pointers и use-after-free.
Проблема:
Raw pointers (*const T, *mut T) не интегрированы в систему владения и контроля ссылок Rust: они могут указывать на невалидную память, быть некорректно освобождены или не освободиться вовсе. Ошибка в работе с ними может привести к UB (undefined behavior), падениям, security-уязвимостям или утечкам памяти.
Решение:
Вместо raw pointers рекомендуется использовать безопасные типы — Box, Rc, Arc, а для временных ссылок — borrow-ссылки. Если же без raw-pointers не обойтись (например, для работы с C API), всю работу оборачивают в unsafe-блоки, тщательно организуют Drop, и по возможности используют crates типа NonNull. Ещё одна техника — RAII-обертки и минимизация жизненного цикла указателя.
Пример кода:
fn allocate_in_heap() -> Box<i32> { Box::new(100) } // память будет освобождена автоматически // с raw pointer unsafe fn leak_memory() { let ptr = libc::malloc(4) as *mut i32; if !ptr.is_null() { *ptr = 42; // libc::free(ptr); // если забыть освободить — утечка! } }
Ключевые особенности:
Гарантирует ли Box автоматическую очистку всех вложенных значений при удалении Box?
Да, при удалении Box<T> деструктор вызывает очистку сначала самой обёртки, затем рекурсивно — всех данных, вложенных внутри (вплоть до элементов Vec или других Box внутри структуры T).
Можно ли безопасно передать raw pointer структуры через несколько функций, не рискуя получить use-after-free?
Нет, raw pointer не несёт информацию о времени жизни объекта. Компилятор не может проверить безопасность, поэтому ответственность целиком лежит на разработчике: если объект освобождён, raw pointer будет указывать в пустоту.
Если вручную используем free или drop_in_place, может ли Rust вызвать Drop дважды по одному и тому же адресу?
Да, если после ручного освобождения оставить другой Box/указатель, ссылающийся на этот же блок, то при уничтожении второго экземпляра Drop вызовется повторно, вызывая UB. Никогда не стоит вручную освобождать то, чем управляет Box, Vec и др.
Программист принимал raw pointer из внешней C-библиотеки, не освободил после использования или perfnodo-dealloc ошибся с временем жизни.
Плюсы:
Минусы:
Используется RAII-обертка с Drop, указатель inkapsuliruetsya через Box или NonNull, всё безопасно уничтожается по окончании scope.
Плюсы:
Минусы: