Есть ли смысл объявлять мапу в конструкторе в Go вместо объявления переменной
Что проверяют
Знаете ли вы, что нулевое значение мапы это nil и что с ним можно, а что нельзя. На самом деле вопрос про то, как сделать тип безопасным, если его создали в обход конструктора. Следом часто спрашивают, почему мапы не потокобезопасны и что с этим делать.
Как отвечать
Начните с поведения nil-мапы: var m map[string]int объявляет мапу без выделенной памяти. Чтение возвращает нулевое значение, len даёт 0, range и delete работают, но запись падает с паникой assignment to entry in nil map. Поэтому пустая мапа создаётся через make(map[string]int) или литерал map[string]int{}, это равнозначно.
Для локальной переменной конструктор не нужен. Смысл появляется, когда мапа живёт полем структуры:
- конструктор
NewCache(): инициализирует поле черезmakeи может сразу задать ёмкостьmake(map[K]V, n), если размер известен, чтобы таблица не перестраивалась при заполнении. Поле лучше сделать неэкспортируемым, иначе пользователь соберёт структуру литералом и получитnil; - ленивая инициализация: в методе записи
if c.m == nil { c.m = make(map[K]V) }. Так нулевое значение типа сразу пригодно к работе, как уsync.Mutexилиbytes.Buffer, и конструктор становится необязательным; - конкурентный доступ: если к мапе обращаются из нескольких горутин, рядом с ней в структуре должен быть
sync.RWMutex, либо используйтеsync.Mapдля сценариев «записали один раз, читаем много».
Полезно знать ещё две вещи. Мапа не уменьшается: после массового delete память под внутренние таблицы остаётся, поэтому большие мапы иногда пересоздают. Очистить мапу целиком можно встроенной функцией clear(m), она появилась в Go 1.21.
Где спотыкаются
Думают, что var m map[string]int уже готова к записи, и получают панику в продакшене на первом же ключе.
Отдают мапу наружу геттером. Мапа устроена как указатель на внутреннюю структуру, поэтому вызывающий код меняет состояние вашего типа в обход методов. Отдавайте копию через maps.Clone.
Пишут в мапу из нескольких горутин без мьютекса. Рантайм это обнаруживает и роняет процесс с fatal error: concurrent map writes, который нельзя перехватить через recover.