Map
Тема дорожной карты · Golang
Словари в Go — встроенная структура данных на основе хеш-таблицы, хранящая неупорядоченные пары ключ-значение и обеспечивающая амортизированные O(1) операции поиска, вставки и удаления, что делает их одним из наиболее часто используемых типов коллекций в Golang-программах. Словарь создаётся через make(map[K]V) или составной литерал map[string]int{"hits": 0}, а двузначное присваивание value, ok := m[key] безопасно проверяет существование ключа без паники. Словари в Go не безопасны для конкурентного использования; когда несколько goroutines читают и пишут в один и тот же словарь, необходимо защищать его с помощью sync.RWMutex из пакета sync или использовать тип sync.Map, введённый для высококонкурентных сценариев. Передача словаря в функцию передаёт ссылку, поэтому мутации внутри функции затрагивают словарь вызывающего кода — распространённый источник ошибок для разработчиков, только начинающих работать с Golang. Понимание словарей в Go фундаментально для построения кешей, таблиц маршрутизации, хранилищ конфигурации и любого бэкенд-сервиса, которому нужен быстрый доступ к данным по ключу.
Как это работает
Map покрывает встроенное: массивы (фиксированный размер — редко в идиоматичном Go), slices (динамические массивы — рабочая лошадка), maps (map[K]V, на хешах), strings (UTF-8 байтовые последовательности, неизменяемые). Struct — пользовательские типы с именованными полями; методы на типах (func (r Receiver) Method()); интерфейсы декларируют наборы методов (удовлетворяются неявно — нет ключевого слова implements). Указатели через & и *; slices и maps уже содержат указатели внутри.
Когда применять
Slices везде, где массивы соблазняют — почти всегда правильный ответ. Map — для lookup по ключу; помните, что Go-map не упорядочены. Embedding struct — для композиции (нет эквивалента наследования). Маленькие интерфейсы (1-3 метода) — Go-интерфейсы лучше когда обнаружены, чем объявлены (выносите, когда есть 2+ реализаций).
Типичные ошибки
Ловушки Map: slice-граблик — append(s, x) может или не может делить underlying массив с s (при реалокации не делит — тихие баги); for _, v := range slice { go func() { use(v) }() } захватывает v по ссылке до 1.22 (closures видят последнее значение — починено в 1.22, но многие туториалы до этого); nil-maps паникуют на запись (make(map[K]V)); огромные interface-типы убивают идиому "implicitly satisfied".