Могут ли указатели на две разные переменные нулевого размера оказаться равными в Go?
Да. Указатели на разные переменные нулевого размера в Go могут иметь одинаковое значение, поэтому сравнение таких указателей не подтверждает, что они указывают на одну и ту же переменную. Это разрешённое поведение языка, а не ошибка компилятора.
Go поддерживает типы нулевого размера, например struct{}, которым не требуется хранить данные. Они полезны для маркеров, сигналов в каналах и элементов множеств, где важен сам факт присутствия, а не содержимое.
Поскольку таким значениям не нужно отдельное хранилище, реализация может не резервировать для каждой переменной уникальный адрес. Это позволяет не тратить память, но исключает использование адреса как универсального идентификатора объекта.
Разработчик может ожидать, что разные переменные всегда имеют разные адреса. Для обычных значений это практически естественное предположение, но для объектов нулевого размера оно неверно: спецификация допускает равенство указателей на разные такие переменные.
Если использовать адрес struct{} как ключ или как признак уникальности объекта, программа может получить неожиданные совпадения. Логика, зависящая от уникальности адресов, становится непереносимой между компиляторами, архитектурами и вариантами оптимизации.
Для переменных нулевого размера сравнение указателей не даёт гарантии различать переменные. Компилятор или среда выполнения могут предоставить им один и тот же адрес, поскольку чтение или запись полезных данных через этот адрес отсутствует.
Результат сравнения может быть как true, так и false. Даже если в конкретной сборке напечатано false, это не превращается в гарантию уникальности адресов; другая сборка вправе вести себя иначе.
Само разыменование указателя корректно в обычных рамках типовой безопасности: значение имеет нулевой размер, поэтому фактически нечего извлекать. Однако указатель на struct{} не следует применять как уникальный токен идентичности.
Если нужна уникальность, используйте объект с ненулевым состоянием, явный счётчик идентификаторов или другой механизм, который прямо задаёт требование уникальности. Если нужен только маркер без данных, struct{} остаётся подходящим выбором, но сравнивать его адреса как идентификаторы нельзя.
В сервисе строят множество посещённых объектов и пытаются использовать указатели на пустые структуры как ключи. Вариант с struct{} экономичен по размеру, но адреса разных значений могут совпасть, из-за чего разные объекты будут ошибочно считаться одним.
Можно заменить пустую структуру на bool, но это не решает задачу идентичности: одинаковые значения всё равно не дают уникального ключа, а память расходуется больше. Можно хранить явный числовой идентификатор, что надёжно, но требует управления его выдачей.
Практичное решение — отделить маркер от идентичности: использовать struct{} только как значение множества, а уникальный идентификатор хранить отдельно. В результате сохраняется экономия памяти и исчезает зависимость от адресов.
Нет. Разные переменные нулевого размера могут получить разные адреса, хотя их размер равен нулю. Различие адресов в конкретном запуске ничего не говорит о наличии полезного хранилища и не создаёт обратной гарантии.
struct{} как ключ map?Да, указатель сам по себе является сравнимым типом и может быть ключом map. Но это не означает, что разные указатели на разные переменные нулевого размера будут различаться. Поэтому такой ключ безопасен только тогда, когда программа заранее не требует уникальности адресов.
Да, специфика допускает совпадение указателей на разные переменные именно для объектов нулевого размера. Для переменных ненулевого размера их отдельное размещение не даёт такого специального исключения, поэтому адрес обычно используют для различения объектов, но полагаться на адресную арифметику или время жизни объекта всё равно нельзя без дополнительных гарантий языка.