Где выгодно и безопасно использовать неизменяемые объекты
Что проверяют
Понимаете ли вы, что в Go нет встроенной неизменяемости для структур, и умеете ли обеспечить её сами. Интервьюер ждёт связи с конкурентностью: где неизменяемость избавляет от мьютексов, а где только тратит память. Следом часто просят показать, как безопасно обновлять конфиг, который читают тысячи горутин.
Как отвечать
Сначала честно про язык: const работает только для чисел, строк и булевых значений, неизменяемы по природе строки. Для своих типов неизменяемость строится соглашением:
- неэкспортируемые поля и конструктор:
NewMoney(amount, currency)проверяет данные, снаружи есть только методы чтения, а «изменяющие» методы возвращают новое значение, какtime.Time.Add; - копии на границе: срез и map внутри структуры разделяются при копировании, поэтому геттер отдаёт
slices.Cloneилиmaps.Clone(Go 1.21+), а конструктор копирует входные данные; - снимок и атомарная подмена: конфиг собирается целиком и публикуется через
atomic.Pointer[Config], обновление это новый объект иStore. Читатели берутLoad()без блокировок и работают с согласованным снимком.
Где выгодно: общие данные только для чтения между горутинами (конфиги, справочники, таблицы маршрутизации), ключи map и кешей, доменные значения вроде денег и дат, которые не должны меняться у вас за спиной.
Где невыгодно: горячие циклы и большие структуры, где каждая «модификация» порождает аллокацию и нагружает GC. Классика: склейка строк в цикле через + вместо strings.Builder. Буферы и накопители разумно держать изменяемыми внутри одной горутины.
Где спотыкаются
Считают, что value receiver или передача структуры по значению делает её неизменяемой. Копия поверхностная: срезы, map и указатели внутри остаются общими.
Думают, что неизменяемому объекту синхронизация не нужна вовсе. Сам объект читать безопасно, но переменная с указателем на него, которую перезаписывают, требует atomic или мьютекса, иначе go test -race найдёт гонку.
Тащат из Java Builder и геттеры на всё подряд. В Go обычно хватает конструктора NewX и функциональных опций.