Нельзя &m["k"] (адреса меняются при росте). Порядок итерации рандомизирован.
Конкурентная запись → fatal error (не просто гонка). Go 1.24+ внутри использует Swiss Tables (API не меняется).
core
Интерфейсы
Значение интерфейса — пара (тип, данные). iface (с методами): {tab, data}; eface (пустой): {_type, data}.
Ловушка typed nil: интерфейс == nil только если И тип, И данные nil. Типизированный nil-указатель → интерфейс ≠ nil.
Вызов через интерфейс — динамическая диспетчеризация (~2-5 нс, обычно не инлайнится).
Type assertion: сравнить tab._type с целевым типом; совпало → вернуть data, иначе zero+false (или паника).
core
Ошибки
error — интерфейс с одним методом Error() string. Любой тип с ним удовлетворяет error.
Оборачивание: fmt.Errorf("...: %w", err). Разворачивание: errors.Unwrap / Is / As.
errors.Is — сравнение по значению (сентинелы). errors.As — извлечь тип из цепочки.
Добавляй контекст через %w; обрабатывай ошибку один раз; не игнорируй.
core
Defer
Аргументы defer вычисляются сразу (в момент defer), а не при выполнении.
Отложенные вызовы — LIFO (последний объявлен — первый выполнен).
defer может читать и менять именованные возвращаемые значения.
recover() работает только внутри отложенной функции. 1.14+ open-coded defer — почти без накладных расходов.
runtime
Сборщик мусора
GC — конкурентный, трёхцветный mark-and-sweep. Не поколенческий, не компактирующий, низкие паузы.
Цвета: белый (кандидат на сборку), серый (посещён, дети не сканированы), чёрный (достижим).
Write barrier при записи указателя держит инвариант (чёрный не указывает на белый) во время конкурентной маркировки.
GOGC (100): GC при росте кучи на 100% от живой. GOMEMLIMIT (1.19+) — мягкий лимит памяти.
core
Дженерики
Реализация — гибрид: GC Shape Stenciling + словари. Типы с одинаковой GC-формой делят реализацию (все указатели — одна форма).
Ограничения: any, интерфейсы (fmt.Stringer), объединения (int | float64), приближение ~int (включает типы с базовым int).
Нет: параметров типа у методов, специализации под конкретный тип, вариадических параметров типа.
runtime
Escape-анализ
Компилятор решает: стек (дёшево, авто-освобождение, кэш) или куча (под GC) — по тому, «убегает» ли переменная.
Смотреть решения: go build -gcflags="-m".
Escape вызывают: возврат указателя, упаковка в interface{}, захват замыканием, make неизвестного размера, указатель в канал.
runtime
Выравнивание и false sharing
Поля структуры выравниваются по своему размеру; компилятор вставляет padding. Порядок полей влияет на размер: клади большие поля первыми, мелкие группируй.
Размер структуры = выравнивание по наибольшему полю; на 1М экземпляров лишние 8 байт = 8 МБ. Чинить: go vet -fieldalignment / fieldalignment -fix.
False sharing: два поля в одной 64-байтной кэш-линии, которые пишут разные ядра → инвалидация кэша и просадка. Лечится padding между ними.
Пустая структура struct{} занимает 0 байт; map[K]struct{} — идиома множества, chan struct{} — сигнал без данных.
runtime
unsafe и рефлексия
unsafe.Pointer отслеживается GC и держит объект живым; uintptr — обычное число, GC его не видит → нельзя хранить (объект могут собрать/переместить).
Конвертация uintptr↔Pointer допустима только в ОДНОМ выражении: unsafe.Pointer(uintptr(p)+off). Сохранил uintptr в переменную → UB.