Достаточно ли совпадения числового адреса для разыменования указателя, восстановленного из usize?
Нет. Совпадение числового адреса само по себе не доказывает, что восстановленный raw pointer связан с действующей аллокацией, имеет подходящую provenance и указывает на объект, доступный для чтения или записи. Преобразование адреса обратно в указатель не отменяет требований безопасности: разыменование допустимо только при доказанном времени жизни объекта, корректном типе, выравнивании и границах.
Raw pointers и преобразования указателей в целые числа нужны для низкоуровневого кода: взаимодействия с операционными системами, аппаратными адресами, C-библиотеками и API, которые хранят непрозрачный контекст в машинном слове. В традиционной модели казалось естественным считать указатель просто числом, которое можно сохранить, передать и восстановить.
Однако оптимизирующий компилятор должен учитывать не только числовой адрес, но и происхождение указателя — его связь с конкретной аллокацией и объектом. Современное обсуждение provenance возникло именно для описания случаев, когда два указателя имеют одинаковое числовое значение, но не обладают одинаковыми правами доступа в модели памяти.
Предположим, указатель на объект преобразовали в usize, сохранили, а позднее восстановили. Между этими операциями исходный объект мог быть уничтожен, память могла быть освобождена и повторно выдана другому объекту, а целочисленное значение могло быть изменено арифметикой.
Даже если адрес снова выглядит правдоподобно, это не доказывает корректность доступа. Неверное разыменование может привести к неопределённому поведению: чтению чужого объекта, нарушению требований оптимизатора, повреждению памяти или ошибке, проявляющейся только в релизной сборке.
Важно различать две вещи: сам cast между указателем и usize обычно не является разыменованием и не читает память, но последующее использование результата как указателя требует полного доказательства инвариантов. Целое число не становится безопасным дескриптором объекта автоматически.
У указателя есть не только числовая часть, то есть адрес, но и семантическая связь с областью памяти, из которой он был получен. В терминологии модели памяти это обычно описывают как provenance. Она помогает определить, к какой аллокации указатель относится и какие операции над ней могут быть корректными.
Поэтому для безопасного доступа нужно доказать как минимум следующее:
Преобразование указателя в usize особенно опасно, если число используется как полноценный идентификатор памяти. Арифметика над числом не является заменой типизированного вычисления смещения указателя: она может вывести значение за пределы объекта, потерять связь с аллокацией или создать адрес, который численно выглядит допустимым, но семантически не является указателем на нужный объект.
Операции с целыми числами также не продлевают время жизни объекта. Если исходная память освобождена, восстановление того же адреса не возвращает объект к жизни. Если аллокатор позже выдаст тот же диапазон другому объекту, совпавший адрес не сделает старый указатель пригодным для нового объекта.
Нужно учитывать и платформу. Размер указателя может отличаться от размера usize в экзотических архитектурах и специальных моделях выполнения, а преобразования могут зависеть от ABI. Для обычного Rust-кода usize предназначен для адресоподобных значений текущей платформы, но это не превращает хранение указателя в целое число в переносимый способ управления памятью.
Практическое правило: если требуется сохранить ссылку на Rust-объект, храните сам raw pointer в подходящем типе и явно управляйте временем жизни объекта. Если через FFI нужен непрозрачный идентификатор, лучше передавать указатель как *mut c_void по контракту API либо использовать отдельный целочисленный handle, связанный с таблицей живых объектов. В последнем случае число является ключом, а не адресом, и доступ выполняется после проверки записи в таблице.
Конкретные API и гарантии для работы с адресами и provenance зависят от версии Rust и используемой модели strict provenance. Поэтому нельзя формулировать универсальное правило вида «любое преобразование usize обратно в указатель всегда запрещено» или, наоборот, «совпадения адреса всегда достаточно». На собеседовании ожидается более точный вывод: числовое равенство — лишь одно наблюдаемое свойство, а не доказательство допустимости разыменования.
Библиотека на Rust регистрирует callback в C-библиотеке. C API предоставляет только поле uintptr_t user_data, которое возвращается без изменения при каждом вызове callback. Разработчик сохраняет там адрес состояния Rust, а затем восстанавливает его из числа.
Рассматривались три варианта. Первый — оставить адрес в uintptr_t: это просто и совместимо с конкретным C ABI, но требует строгого контроля времени жизни, платформенных преобразований и правил provenance; ошибка в C может превратить число в произвольный адрес. Второй — передавать указатель через предусмотренное API поле void*: это лучше отражает семантику указателя, но возможно только если C-интерфейс действительно предоставляет такой параметр и не обещает хранить значение как самостоятельное число.
Третий вариант — выдавать C целочисленный handle, а состояние хранить в защищённой таблице Rust. Он требует синхронизации и обработки недействительных handles, зато число больше не интерпретируется как адрес, а освобождение объекта можно связать с удалением записи.
Для нового интерфейса выбран третий вариант, поскольку callback может приходить после обычного пути завершения операции. Таблица позволила явно отслеживать состояние регистрации, отклонять устаревший handle и не полагаться на совпадение адресов. Для уже заданного ABI, где изменение uintptr_t невозможно, допустим первый вариант только при документированном контракте: C не изменяет значение, Rust гарантирует жизнь объекта до последнего callback, а все преобразования проверены для целевых платформ.
usize время жизни объекта?Нет. Время жизни определяется владельцем и правилами управления объектом, а не наличием числового значения, похожего на адрес. Если объект уничтожен, сохранённое число не удерживает аллокацию и не может безопасно использоваться для доступа к прежнему объекту.
Это особенно важно для Box, Vec и других владельцев: перемещение самого владельца обычно не уничтожает объект, но освобождение владельца завершает время жизни выделенной памяти. Для FFI нужно отдельно договориться, кто удерживает объект живым и какое событие прекращает возможность callback.
usize указатель полностью безопасным?Нет. Неизменённое число устраняет один риск — арифметическое повреждение адреса, — но не доказывает остальные инварианты. Нужно по-прежнему установить, что объект жив, память принадлежит нужной аллокации, указатель правильно выровнен, тип соответствует объекту, а доступ разрешён правилами Rust.
В конкретном ABI и на конкретных платформах такой round trip может быть частью рабочего FFI-контракта. Но это практическая договорённость, а не универсальная замена проверке безопасности; при переносе кода или изменении модели памяти предположение должно быть пересмотрено.
Нет. Совпавший адрес не превращает указатель на старую аллокацию в указатель на новую. Старый указатель связан с завершившимся временем жизни прежнего объекта, а обращение через него остаётся использованием недействительного указателя.
Нужно получить новый указатель из нового живого объекта и доказать его свойства заново. Именно поэтому таблица handles безопаснее хранения адресов: после удаления записи старый handle можно отклонить, даже если аллокатор позже использует тот же числовой адрес для другой памяти.