go/prepmiddle+ interview
7 модулейby @ernazar
темы:
core

Строки

  • Строка — 2-словная структура {Data, Len} (16 байт на 64-бит). Неизменяема, UTF-8, без нуль-терминатора.
  • Срез строки делит базовый массив → маленький срез большой строки держит её всю в памяти (утечка).
  • Много конкатенаций → strings.Builder с Grow(), а не += в цикле (O(n²)).
  • string vs []byte: string неизменяем (16 байт), []byte изменяем (24 байта, есть cap).
core

Слайсы

  • Слайс — заголовок {ptr, len, cap} (24 байта). nil-слайс: ptr=nil. empty: ptr≠nil. Оба len=cap=0.
  • append при len==cap делает реаллокацию и копирование → новый массив. Рост (1.18+): <256 ×2, ≥256 ~+25%.
  • Срезы делят базовый массив → append в один может молча перезаписать другой (aliasing).
  • Полное выражение среза s[lo:hi:max] ограничивает cap → форсирует новую аллокацию и убирает aliasing.
core

Мапы

  • Мапа — указатель на hmap; данные в бакетах по 8 слотов (tophash + 8 ключей + 8 значений + overflow).
  • Рост: коэф. загрузки >6.5 → удвоение бакетов; много overflow → реорганизация. Эвакуация инкрементальная.
  • Нельзя &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.
  • Zero-copy string↔[]byte (Go 1.20): unsafe.String/StringData/Slice/SliceData. Полученный []byte НЕЛЬЗЯ мутировать (строка неизменяема).
  • Рефлексия в 10-200× медленнее прямого доступа и аллоцирует. Кэшируй метаданные (индекс поля) или генерируй код; избегай в горячих путях.