Практическая ситуация: интерфейс получил указатель на объект, после чего объект изменили через исходную переменную. Какой результат даст вызов метода через интерфейс?
Вызов метода через интерфейс увидит изменённое состояние объекта. Интерфейс хранит копию указателя на тот же объект, а не снимок его полей, поэтому исходная переменная и интерфейс обращаются к одной памяти.
Интерфейсы в Go отделяют контракт поведения от конкретной реализации. Это позволяет передавать объект коду, которому важны только его методы, сохраняя при этом обычную семантику значений и указателей Go.
Такой подход особенно полезен для внедрения зависимостей, тестирования и работы с разными реализациями одного поведения. Однако интерфейс не превращает указатель в независимую копию объекта.
Если разработчик воспринимает присваивание указателя интерфейсу как копирование объекта, он может ошибочно ожидать неизменное состояние. На практике изменение объекта через любую из имеющихся ссылок будет видно через интерфейс.
Это может быть как намеренным способом совместно изменять состояние, так и источником трудноуловимых ошибок. При конкурентном доступе к тому же объекту без синхронизации возможна гонка данных.
Значение интерфейса концептуально содержит динамический тип и динамическое значение. Если в интерфейс записан указатель, динамическое значение — это копия самого указателя, указывающая на тот же экземпляр структуры.
Поэтому копируется адрес, но не объект по этому адресу. Вызов метода через интерфейс использует сохранённый указатель и читает актуальные поля объекта.
После присваивания c.N = 2 метод через r возвращает 2, потому что c и динамическое значение интерфейса указывают на один объект.
Это отличается от помещения в интерфейс самого значения структуры: тогда интерфейс получает копию структуры на момент присваивания. Изменение исходной переменной после этого не изменит уже сохранённую копию.
Преимущество указателя — возможность совместно наблюдать и изменять состояние без копирования объекта. Компромиссы — повышенная связанность через общее состояние, необходимость учитывать время жизни объекта и возможные проблемы синхронизации.
Сервис передаёт в несколько компонентов интерфейс чтения состояния объекта. Один компонент обновляет поля объекта, а остальные получают интерфейс и вызывают его методы. Если интерфейсы содержат указатель на объект, все компоненты видят актуальные данные.
Рассматривались два варианта. Передавать структуры по значению безопаснее с точки зрения изоляции состояния, но изменения не распространяются между компонентами и крупные объекты копируются. Передавать указатель эффективнее и позволяет централизованно менять состояние, но требует определить владельца объекта и правила синхронизации.
Выбран указатель за интерфейсом, поскольку состояние должно быть общим. Для конкурентных операций добавили синхронизацию внутри реализации методов, а интерфейс оставили узким — только с необходимыми операциями чтения.
Нет. Интерфейс получит копию значения структуры. Последующее изменение исходной переменной не изменит эту копию, хотя поля внутри неё могут содержать указатели, карты или срезы с собственной ссылочной семантикой.
Нет. Копируется значение указателя, то есть адрес объекта. Поэтому несколько интерфейсных значений, содержащих указатель на один экземпляр, будут наблюдать изменения одного и того же объекта.
Сам факт хранения указателя в интерфейсе потокобезопасности не обеспечивает. Если одна горутина изменяет поля, а другая читает их без подходящей синхронизации, возникает гонка данных. Защиту нужно реализовать в конкретном типе, например с помощью sync.Mutex или другого подходящего механизма.