Senior Go Interview Prep - Core Go: https://go.vbloher.org/docs/01-core-go/ - Механика defer в Go: https://go.vbloher.org/docs/01-core-go/defer/ - Встраивание структур и интерфейсов (Embedding): https://go.vbloher.org/docs/01-core-go/embedding/ - Ошибки в Go: error, wrapping, errors.Is/As/Join: https://go.vbloher.org/docs/01-core-go/errors/ - Дженерики в Go (1.18+): https://go.vbloher.org/docs/01-core-go/generics/ - Интерфейсы в Go: https://go.vbloher.org/docs/01-core-go/interfaces/ - Устройство map в Go: https://go.vbloher.org/docs/01-core-go/maps/ - panic / recover: механика, раскрутка стека и runtime-паники: https://go.vbloher.org/docs/01-core-go/panic-recover/ - Указатели в Go: https://go.vbloher.org/docs/01-core-go/pointers/ - Рефлексия в Go (reflect): https://go.vbloher.org/docs/01-core-go/reflection/ - Внутреннее устройство слайсов в Go: https://go.vbloher.org/docs/01-core-go/slices/ - Строки, руны и байты в Go: https://go.vbloher.org/docs/01-core-go/strings-runes-bytes/ - Система типов Go: defined types, alignment, memory layout: https://go.vbloher.org/docs/01-core-go/type-system/ - Concurrency: https://go.vbloher.org/docs/02-concurrency/ - sync/atomic: https://go.vbloher.org/docs/02-concurrency/atomic/ - Буферизованные vs небуферизованные каналы: https://go.vbloher.org/docs/02-concurrency/buffered-unbuffered/ - Канал vs Mutex: когда что выбрать: https://go.vbloher.org/docs/02-concurrency/channel-vs-mutex/ - Каналы: устройство hchan: https://go.vbloher.org/docs/02-concurrency/channels/ - Утечки горутин, дедлоки, livelock, starvation: https://go.vbloher.org/docs/02-concurrency/common-leaks-deadlocks/ - sync.Cond: https://go.vbloher.org/docs/02-concurrency/cond/ - context: https://go.vbloher.org/docs/02-concurrency/context/ - Горутины: жизненный цикл, стоимость, стек: https://go.vbloher.org/docs/02-concurrency/goroutines-lifecycle/ - sync.Mutex и sync.RWMutex: https://go.vbloher.org/docs/02-concurrency/mutex-rwmutex/ - sync.Once: https://go.vbloher.org/docs/02-concurrency/once/ - Паттерны конкурентности: https://go.vbloher.org/docs/02-concurrency/patterns/ - Race Detector (гонки данных и -race): https://go.vbloher.org/docs/02-concurrency/race-detector/ - Планировщик GMP: https://go.vbloher.org/docs/02-concurrency/scheduler-gmp/ - select: https://go.vbloher.org/docs/02-concurrency/select/ - sync.WaitGroup: https://go.vbloher.org/docs/02-concurrency/waitgroup/ - Runtime и память: https://go.vbloher.org/docs/03-runtime-memory/ - Паттерны аллокаций и снижение давления на GC: https://go.vbloher.org/docs/03-runtime-memory/allocation-patterns/ - Escape Analysis: когда переменная убегает в кучу: https://go.vbloher.org/docs/03-runtime-memory/escape-analysis/ - Сборщик мусора Go: concurrent tri-color mark-sweep: https://go.vbloher.org/docs/03-runtime-memory/gc/ - Тюнинг GC: GOGC и GOMEMLIMIT: https://go.vbloher.org/docs/03-runtime-memory/gogc-gomemlimit/ - GOMAXPROCS: параллелизм планировщика и проблема контейнеров: https://go.vbloher.org/docs/03-runtime-memory/gomaxprocs/ - Утечки горутин (goroutine leaks): https://go.vbloher.org/docs/03-runtime-memory/goroutine-leaks/ - Утечки памяти в Go (несмотря на GC): https://go.vbloher.org/docs/03-runtime-memory/memory-leaks/ - Модель памяти Go (Go Memory Model): happens-before и синхронизация: https://go.vbloher.org/docs/03-runtime-memory/memory-model/ - pprof: профилирование CPU, памяти и блокировок в Go: https://go.vbloher.org/docs/03-runtime-memory/pprof/ - Execution Tracer и runtime/trace: тайминги вместо агрегатов: https://go.vbloher.org/docs/03-runtime-memory/runtime-tracing/ - Стек vs Куча: где живут данные в Go: https://go.vbloher.org/docs/03-runtime-memory/stack-vs-heap/ - Тестирование: https://go.vbloher.org/docs/04-testing/ - testify, assert/require и golden files: https://go.vbloher.org/docs/04-testing/assertions-testify/ - Бенчмарки в Go: https://go.vbloher.org/docs/04-testing/benchmarks/ - Покрытие, -race и флаки-тесты: https://go.vbloher.org/docs/04-testing/coverage-race/ - Нативный fuzzing в Go (1.18+): https://go.vbloher.org/docs/04-testing/fuzzing/ - Интеграционные тесты, testcontainers-go, TestMain: https://go.vbloher.org/docs/04-testing/integration-testcontainers/ - Моки, стабы и тестируемость: https://go.vbloher.org/docs/04-testing/mocks/ - Table-driven тесты, subtests и параллельность: https://go.vbloher.org/docs/04-testing/table-driven/ - Backend: https://go.vbloher.org/docs/05-backend/ - Аутентификация и авторизация: AuthN/AuthZ, сессии vs токены, RBAC/ABAC, API keys, mTLS, секреты: https://go.vbloher.org/docs/05-backend/auth-authz/ - Graceful Shutdown HTTP/gRPC сервера в Go: https://go.vbloher.org/docs/05-backend/graceful-shutdown/ - gRPC: типы RPC, интерсепторы, контекст, метаданные, error model: https://go.vbloher.org/docs/05-backend/grpc/ - JWT (JSON Web Token): https://go.vbloher.org/docs/05-backend/jwt/ - Middleware-паттерн в Go: https://go.vbloher.org/docs/05-backend/middleware/ - net/http: Server, Handler, ServeMux, таймауты, Client и контекст: https://go.vbloher.org/docs/05-backend/net-http/ - OAuth2: роли, grant types, OIDC, токены и типовые ошибки: https://go.vbloher.org/docs/05-backend/oauth2/ - OpenAPI/Swagger, code generation, contract-first vs code-first, валидация: https://go.vbloher.org/docs/05-backend/openapi/ - Protocol Buffers: схемы, wire format, эволюция и совместимость: https://go.vbloher.org/docs/05-backend/protobuf/ - REST: принципы, версионирование, идемпотентность, статусы, пагинация, ошибки: https://go.vbloher.org/docs/05-backend/rest/ - Сети и протоколы: https://go.vbloher.org/docs/06-networking/ - Пулы соединений: http.Transport, БД, утечки: https://go.vbloher.org/docs/06-networking/connection-pooling/ - DNS: записи, резолвинг, кэширование, DNS в Go: https://go.vbloher.org/docs/06-networking/dns/ - Версии HTTP: 1.1, 2, 3: https://go.vbloher.org/docs/06-networking/http-versions/ - TCP/IP: модель, транспорт и что важно бэкендеру: https://go.vbloher.org/docs/06-networking/tcp-ip/ - TLS: handshake, сертификаты, mTLS, производительность: https://go.vbloher.org/docs/06-networking/tls/ - UDP и надёжность поверх UDP: https://go.vbloher.org/docs/06-networking/udp/ - WebSocket: upgrade, фреймы, масштабирование: https://go.vbloher.org/docs/06-networking/websocket/ - Базы данных: https://go.vbloher.org/docs/07-databases/ - Пул соединений к PostgreSQL в Go: database/sql, pgx, pgxpool, PgBouncer: https://go.vbloher.org/docs/07-databases/connection-pooling-pgx/ - Взаимоблокировки (Deadlocks) в PostgreSQL: https://go.vbloher.org/docs/07-databases/deadlocks/ - Индексы в PostgreSQL: https://go.vbloher.org/docs/07-databases/indexes/ - Уровни изоляции транзакций в PostgreSQL: https://go.vbloher.org/docs/07-databases/isolation-levels/ - MVCC в PostgreSQL: версии строк, видимость, VACUUM и bloat: https://go.vbloher.org/docs/07-databases/mvcc/ - Обзор NoSQL и Redis: https://go.vbloher.org/docs/07-databases/nosql-redis/ - Партиционирование таблиц в PostgreSQL: https://go.vbloher.org/docs/07-databases/partitioning/ - Архитектура PostgreSQL: https://go.vbloher.org/docs/07-databases/postgresql-architecture/ - Планирование и оптимизация запросов в PostgreSQL: https://go.vbloher.org/docs/07-databases/query-planning/ - Репликация в PostgreSQL: https://go.vbloher.org/docs/07-databases/replication/ - Шардирование (горизонтальное масштабирование): https://go.vbloher.org/docs/07-databases/sharding/ - Транзакции в PostgreSQL и Go (database/sql, pgx): https://go.vbloher.org/docs/07-databases/transactions/ - Распределённые системы: https://go.vbloher.org/docs/08-distributed-systems/ - CAP теорема: https://go.vbloher.org/docs/08-distributed-systems/cap-theorem/ - Circuit Breaker: https://go.vbloher.org/docs/08-distributed-systems/circuit-breaker/ - Консенсус и Raft: репликация состояния в присутствии отказов: https://go.vbloher.org/docs/08-distributed-systems/consensus-raft/ - Модели согласованности: https://go.vbloher.org/docs/08-distributed-systems/consistency/ - Гарантии доставки сообщений: at-most-once / at-least-once / exactly-once: https://go.vbloher.org/docs/08-distributed-systems/delivery-guarantees/ - Eventual Consistency: https://go.vbloher.org/docs/08-distributed-systems/eventual-consistency/ - Идемпотентность в распределённых системах: https://go.vbloher.org/docs/08-distributed-systems/idempotency/ - Apache Kafka: https://go.vbloher.org/docs/08-distributed-systems/kafka/ - Transactional Outbox: https://go.vbloher.org/docs/08-distributed-systems/outbox/ - RabbitMQ: AMQP 0-9-1, маршрутизация, надёжность доставки и сравнение с Kafka: https://go.vbloher.org/docs/08-distributed-systems/rabbitmq/ - Ретраи: backoff, jitter, budgets и идемпотентность: https://go.vbloher.org/docs/08-distributed-systems/retries/ - Saga Pattern: https://go.vbloher.org/docs/08-distributed-systems/saga/ - Observability: https://go.vbloher.org/docs/09-observability/ - Grafana: https://go.vbloher.org/docs/09-observability/grafana/ - Метрики: RED, USE, Golden Signals: https://go.vbloher.org/docs/09-observability/metrics/ - OpenTelemetry: https://go.vbloher.org/docs/09-observability/opentelemetry/ - Prometheus: https://go.vbloher.org/docs/09-observability/prometheus/ - SLI / SLO / SLA: https://go.vbloher.org/docs/09-observability/slo-sli/ - Структурированное логирование (slog): https://go.vbloher.org/docs/09-observability/structured-logging/ - Distributed Tracing: https://go.vbloher.org/docs/09-observability/tracing/ - System Design: https://go.vbloher.org/docs/10-system-design/ - Analytics Pipeline: https://go.vbloher.org/docs/10-system-design/analytics-pipeline/ - Chat System: https://go.vbloher.org/docs/10-system-design/chat/ - Фреймворк System Design интервью: https://go.vbloher.org/docs/10-system-design/framework/ - Notification Service: https://go.vbloher.org/docs/10-system-design/notification-service/ - Order Service: https://go.vbloher.org/docs/10-system-design/order-service/ - Payment Service: https://go.vbloher.org/docs/10-system-design/payment-service/ - Rate Limiter: https://go.vbloher.org/docs/10-system-design/rate-limiter/ - URL Shortener: https://go.vbloher.org/docs/10-system-design/url-shortener/ - DevOps: https://go.vbloher.org/docs/11-devops/ - CI/CD: пайплайны, стадии, стратегии деплоя: https://go.vbloher.org/docs/11-devops/cicd/ - Облака (AWS / GCP) для бэкендера: https://go.vbloher.org/docs/11-devops/cloud-aws-gcp/ - Docker для Go-разработчика: https://go.vbloher.org/docs/11-devops/docker/ - GitHub Actions и GitLab CI: https://go.vbloher.org/docs/11-devops/github-gitlab-ci/ - Kubernetes для Go-разработчика: https://go.vbloher.org/docs/11-devops/kubernetes/ - Terraform / Infrastructure as Code: https://go.vbloher.org/docs/11-devops/terraform/ - Алгоритмы: https://go.vbloher.org/docs/12-algorithms/ - Типовые алгоритмические задачи и паттерны: https://go.vbloher.org/docs/12-algorithms/common-problems/ - Асимптотическая сложность (Big-O): https://go.vbloher.org/docs/12-algorithms/complexity/ - Структуры данных в Go: https://go.vbloher.org/docs/12-algorithms/data-structures/ - Специфика live-coding на Go: https://go.vbloher.org/docs/12-algorithms/go-specifics/ - Behavioral: https://go.vbloher.org/docs/13-behavioral/ - Конфликты, разногласия и работа со стейкхолдерами: https://go.vbloher.org/docs/13-behavioral/conflicts/ - Как проходит senior-интервью: этапы, оценка, оффер: https://go.vbloher.org/docs/13-behavioral/interview-flow/ - Лидерство и менторство: https://go.vbloher.org/docs/13-behavioral/leadership-mentoring/ - Типовые поведенческие вопросы для Senior: https://go.vbloher.org/docs/13-behavioral/senior-questions/ > Модуль: Core Go · Уровень: Senior ## TL;DR Go — статически типизированный язык со структурной концепцией underlying type, на которой строятся правила assignability и convertibility. `type Foo int` создаёт новый **defined (named) type** с тем же underlying type, но другой identity; `type Foo = int` — это **alias**, полностью идентичный `int`. Поверх системы типов лежит физический memory layout: компилятор выравнивает поля структур по их alignment, добавляя padding, поэтому порядок полей напрямую влияет на `Sizeof`. Знание zerobase для `struct{}`, правил comparable-типов и iota — обязательный senior-минимум. ## Теория ### Defined (named) types vs type aliases В Go есть два разных механизма объявления типов, и их путают чаще всего. ```go type Celsius float64 // defined type: новый тип, identity != float64 type Alias = float64 // alias: то же самое имя для float64 ``` | | `type Foo int` (defined) | `type Foo = int` (alias) | |---|---|---| | Новый тип? | Да, новая type identity | Нет, просто другое имя | | Underlying type | `int` | `int` | | Можно объявлять методы? | Да | Только если базовый тип в том же пакете | | `reflect.TypeOf().Name()` | `"Foo"` | `"int"` | | Совместимость с `int` без конверсии | Нет (нужна явная конверсия в большинстве случаев) | Полная, это одно и то же | **Defined type** получает собственную identity. Даже если underlying type совпадает, два defined-типа не присваиваются друг другу без явной конверсии: ```go type Meters int type Feet int var m Meters = 10 var f Feet = m // ОШИБКА: cannot use m (Meters) as Feet var f2 Feet = Feet(m) // OK, явная конверсия ``` Defined type **не наследует методы** базового типа (если базовый — defined type с методами), но наследует поля и методы, если underlying — структура или встроенный интерфейс через embedding. Важный нюанс: `type T2 T1`, где у `T1` есть методы, — `T2` **не получает** методы `T1`. А вот `type T2 = T1` (alias) — получает, т.к. это тот же тип. **Type alias** появился в Go 1.9 в первую очередь для постепенного рефакторинга больших кодовых баз (перенос типа между пакетами без слома кода). Самый известный пример из stdlib — `byte = uint8` и `rune = int32`. Это встроенные алиасы, поэтому `byte` и `uint8` взаимозаменяемы абсолютно. ```go type byte = uint8 type rune = int32 ``` С Go 1.24 алиасы могут быть generic: `type Set[T comparable] = map[T]struct{}`. ### Underlying type Underlying type — фундамент всей системы типов. Алгоритм определения: - Для предобъявленных типов (`int`, `string`, ...) и литералов типов (`struct{...}`, `[]T`, `map[K]V`) underlying type — это сам тип. - Для defined type underlying type — это underlying type типа, указанного в объявлении (рекурсивно). ```go type A []int // underlying = []int type B A // underlying = []int (НЕ A!) type C B // underlying = []int ``` Underlying type определяет: - какие операции допустимы (можно ли индексировать, делать range, складывать), - правила assignability и convertibility, - удовлетворение constraint'ам в дженериках (`~[]int` матчит любой тип с underlying `[]int`). ### Assignability (присваиваемость) Значение `x` типа `V` присваивается переменной типа `T`, если хотя бы одно из (основные правила): 1. `V` и `T` идентичны. 2. `V` и `T` имеют **идентичный underlying type**, и хотя бы один из них **не является defined type**. 3. `T` — интерфейс, и `V` реализует `T`. 4. `V` — двунаправленный канал, `T` — канал с совместимым направлением и идентичным элементом, и один из них не defined. 5. `x` — предобъявленный `nil`, а `T` — pointer/func/slice/map/chan/interface. 6. `x` — нетипизированная константа, представимая значением типа `T`. Правило 2 — ключевое и неочевидное: ```go type MySlice []int var ms MySlice var s []int = ms // OK! []int не defined type, underlying совпадает ms = s // OK по той же причине type A int type B int var a A var b B = a // ОШИБКА: оба defined types, underlying совпадает, но правило 2 не работает ``` ### Convertibility (конвертируемость) Конверсия `T(x)` допустима, если (основные случаи): - `x` присваивается `T` (assignability ⊂ convertibility); - `V` и `T` имеют идентичный underlying type (игнорируя теги структур); - оба — указатели на типы с идентичным underlying (игнорируя теги, с Go 1.8); - оба числовые (int/float/complex) — возможна потеря данных; - `string` ↔ `[]byte` / `[]rune` / целое (rune → string даёт символ Unicode); - срез → массив или указатель на массив (Go 1.20: `[]T` → `[N]T`, до этого только `*[N]T`). Конверсия **числовых типов** — это truncation/округление, а не паника: ```go var i int32 = 300 var b byte = byte(i) // 300 % 256 = 44, молча var f float64 = 3.99 var n int = int(f) // 3, отбрасывание дробной части (truncation к нулю) ``` Конверсия `string ↔ []byte` по умолчанию **копирует** данные (т.к. string иммутабельна). Компилятор оптимизирует частные случаи: `[]byte(s)` в `range`, ключ map (`m[string(b)]`), сравнение — без аллокации. Для явного безопасного zero-copy в Go 1.20+ есть `unsafe.StringData`/`unsafe.SliceData`/`unsafe.String`/`unsafe.Slice`. ```go s := "hello" b := []byte(s) // копия, отдельная аллокация b[0] = 'H' // не затрагивает s, т.к. это копия ``` ### Alignment и memory layout структур Каждый тип имеет **alignment** (выравнивание) — адрес значения должен быть кратен ему. На amd64/arm64 типично: | Тип | Размер | Alignment | |---|---|---| | bool, int8, uint8 | 1 | 1 | | int16, uint16 | 2 | 2 | | int32, uint32, float32 | 4 | 4 | | int64, uint64, float64, int, uint, pointer | 8 | 8 (на 64-бит) | | string | 16 (ptr+len) | 8 | | slice | 24 (ptr+len+cap) | 8 | | interface | 16 (type+data) | 8 | Компилятор раскладывает поля структуры **в объявленном порядке**, вставляя **padding**, чтобы каждое поле начиналось с адреса, кратного его alignment. Alignment всей структуры = max alignment её полей. Размер структуры округляется вверх до кратного её alignment (trailing padding) — это нужно, чтобы в массиве структур каждый элемент тоже был выровнен. #### Пример: порядок полей влияет на размер ```go type Bad struct { a bool // 1 байт, offset 0 // 7 байт padding (b требует выравнивания по 8) b int64 // 8 байт, offset 8 c bool // 1 байт, offset 16 // 7 байт trailing padding (alignment структуры = 8) } // Sizeof = 24 type Good struct { b int64 // offset 0 a bool // offset 8 c bool // offset 9 // 6 байт trailing padding } // Sizeof = 16 ``` Правило оптимизации: **сортируй поля от большего alignment/размера к меньшему**. Это минимизирует padding. Экономия 8 байт на структуру = мегабайты в слайсе из миллионов элементов и меньше давления на кэш/GC. Ещё пример с тремя int32: ```go type T struct { a int32 // offset 0 b int32 // offset 4 c int32 // offset 8 } // Sizeof = 12, alignment = 4 ``` #### unsafe.Sizeof / Alignof / Offsetof ```go import "unsafe" unsafe.Sizeof(Bad{}) // 24 — размер в байтах (compile-time константа) unsafe.Alignof(Bad{}) // 8 unsafe.Offsetof(Bad{}.b) // 8 — смещение поля от начала структуры ``` Все три — compile-time константы (кроме случаев с variable-length, которых в Go нет). `Sizeof` возвращает **shallow** размер: для slice это 24 (заголовок), а не размер backing array; для string — 16, не длина данных. ### Пустая структура struct{} и zerobase `struct{}` занимает **0 байт**: `unsafe.Sizeof(struct{}{}) == 0`. Это используется когда нужен факт наличия, а не данные. Под капотом: все нулевого размера значения (`struct{}`, `[0]int`) указывают на единственную глобальную переменную `runtime.zerobase`. Поэтому взятие адреса `&struct{}{}` валидно и обычно даёт один и тот же адрес — аллокации кучи не происходит для самих данных. ```go // Set на map: значение не занимает память set := map[string]struct{}{} set["a"] = struct{}{} _, ok := set["a"] // ok == true // Сигнальный канал done := make(chan struct{}) close(done) ``` Нюанс: для полей структуры нулевого размера, стоящих **последними**, компилятор (с Go 1.5+) может добавить padding-байт, если структура иначе была бы нулевого размера, но имеет другие поля — чтобы взятие адреса последнего поля не выходило за границу аллокации. Для самостоятельного `struct{}` размер строго 0. ### iota `iota` — встроенный счётчик константных деклараций внутри `const`-блока. Сбрасывается в 0 в начале каждого `const (...)` и увеличивается на 1 на каждой строке ConstSpec (не на каждый идентификатор). ```go const ( A = iota // 0 B // 1 (выражение повторяется: = iota) C // 2 ) ``` Выражения с iota, пропуски через `_`, битовые флаги: ```go const ( _ = iota // 0, пропускаем KB = 1 << (10 * iota) // 1 << 10 MB // 1 << 20 GB // 1 << 30 ) // Битовые флаги type Flag uint const ( Read Flag = 1 << iota // 1 Write // 2 Exec // 4 ) var perm = Read | Write // 3 ``` Тонкости: - iota увеличивается на каждой строке, даже если в ней несколько констант: `const ( a, b = iota, iota+1; c, d )` → a=0,b=1,c=1,d=2. - В одной строке iota одинаков для всех идентификаторов. - Пропуск строки невозможен без объявления — используйте `_`. ### Типы как значения, comparable, zero values **Comparable типы** (можно `==`/`!=` и использовать как ключ map): - booleans, числа, strings, pointers, channels; - interface-типы (сравниваются по динамическому типу+значению — паника в рантайме, если динамический тип не comparable); - структуры, если **все** поля comparable; - массивы, если элемент comparable. **Не comparable**: slices, maps, functions (только с `nil`). Сравнение паникует/не компилируется: ```go type Key struct{ a int; b string } // comparable, можно ключом map type Bad struct{ s []int } // НЕ comparable var x any = []int{1} var y any = []int{1} _ = x == y // компилируется, но ПАНИКА в рантайме: comparing uncomparable type []int ``` В дженериках constraint `comparable` (Go 1.20+) допускает типы, чьё `==` может паниковать в рантайме (например `any`), — это намеренно расширили. **Zero value** — значение по умолчанию при объявлении без инициализации. Память всегда обнуляется: | Тип | Zero value | |---|---| | числа | `0` | | bool | `false` | | string | `""` (не nil!) | | pointer, slice, map, chan, func, interface | `nil` | | struct | структура из zero value всех полей | | array | массив из zero value элементов | Важно: zero value слайса/мапы — `nil`, но `nil`-слайс можно читать/`append`/`len`/`range`; `nil`-мапу можно читать, но запись паникует. ## Подводные камни / gotchas - **Defined type не получает методы базового типа.** `type MyInt int` от типа с методами не унаследует их; alias — унаследует. - **Правило assignability №2** легко спутать: `[]int` ↔ `MySlice` работает (один не defined), но `A` ↔ `B` (оба defined) — нет. - **Числовая конверсия молча теряет данные** (truncation), без паники и предупреждения. `byte(300)` → 44. - **`string([]int{65})` ≠ "65"**: конверсия среза int не определена; а `string(rune(65))` → "A" (Unicode code point). `string(65)` (с константой) тоже даёт "A", и `go vet` это предупреждает. - **`unsafe.Sizeof` shallow**: для slice даёт 24, не размер данных. Частая ошибка при оценке памяти. - **Порядок полей** в часто-аллоцируемых структурах: неоптимальный порядок раздувает память на десятки процентов. - **Сравнение интерфейсов с не-comparable динамикой** компилируется, но паникует в рантайме. - **`string` zero value — `""`, а не `nil`**; nil к string неприменим, в отличие от slice. - **iota не сбрасывается между ConstSpec одного блока**, только между блоками; и считает строки, а не идентификаторы. - **map со значением `struct{}` vs `bool`**: `struct{}` экономит память, но `bool`-set читаемее; не оптимизируйте преждевременно. - **Padding виден в `unsafe.Offsetof`**: можно проверять layout в тестах для performance-critical структур. ## Вопросы на собеседовании **В:** В чём разница между `type T1 int` и `type T2 = int`? **О:** `type T1 int` создаёт новый defined type с собственной identity: его нельзя присвоить `int` без конверсии, можно объявлять методы, `reflect` покажет имя `T1`. `type T2 = int` — alias, полностью идентичен `int`: те же методы, та же identity, взаимозаменяемы. Алиасы введены в 1.9 для рефакторинга/переноса типов между пакетами. **В:** Что такое underlying type и зачем он нужен? **О:** Underlying type определяется рекурсивно: для предобъявленных типов и литералов — это они сами, для defined type — underlying type его базы. Он управляет допустимыми операциями, правилами assignability/convertibility и удовлетворением constraint'ам `~T` в дженериках. Например `type B A; type C B` — underlying у обоих `[]int`, если `A` это `[]int`. **В:** Почему `var s []int = mySlice` работает, а `var b B = a` (оба defined) — нет? **О:** По правилу assignability: значения присваиваются, если underlying типы идентичны и **хотя бы один не является defined type**. `[]int` — не defined, поэтому MySlice ↔ []int работает. Два defined типа A и B с одинаковым underlying не присваиваются без явной конверсии. **В:** Почему перестановка полей структуры меняет её размер? Покажите пример. **О:** Компилятор выравнивает каждое поле по его alignment, вставляя padding. `{bool; int64; bool}` = 24 байта (7 байт padding после первого bool + 7 trailing). `{int64; bool; bool}` = 16. Правило: сортировать поля от большего alignment к меньшему. Alignment структуры = max по полям, общий размер округляется вверх до кратного alignment (чтобы массивы оставались выровненными). **В:** Чему равен `unsafe.Sizeof` для slice и string? Почему? **О:** Slice — 24 байта (ptr 8 + len 8 + cap 8), string — 16 (ptr 8 + len 8) на 64-бит. Это размер заголовка, а не данных — Sizeof shallow. Backing array лежит отдельно в куче/стеке. **В:** Сколько байт занимает `struct{}{}` и где это применяют? **О:** 0 байт. Все zero-size значения указывают на `runtime.zerobase`, поэтому самих данных не аллоцируют. Применяют для set'ов (`map[T]struct{}`), сигнальных каналов (`chan struct{}`), маркеров. Экономит память по сравнению с `bool`-значением. **В:** Как работает iota? Что выведет блок с пропуском строки? **О:** iota — счётчик ConstSpec в const-блоке, сбрасывается в 0 в начале блока, +1 на каждую строку (не на каждый идентификатор). Пустых строк нет — пропуск делают через `_ = iota`. В одной строке у всех идентификаторов один iota. Классика — битовые флаги `1 << iota` и единицы размера `1 << (10*iota)`. **В:** Какие типы comparable? Что произойдёт при сравнении интерфейсов с разным содержимым? **О:** Comparable: числа, bool, string, pointer, chan, массивы и структуры из comparable полей, интерфейсы. Не comparable: slice, map, func (только с nil). Интерфейсы сравниваются по динамическому типу и значению; если динамический тип не comparable (например slice внутри `any`), сравнение **компилируется, но паникует в рантайме**. **В:** Что вернёт `string(65)` и `[]byte("ab")`? Есть ли аллокация? **О:** `string(rune(65))` → `"A"` (Unicode code point U+0041), не "65"; `go vet` ругается на `string(int)`. `[]byte("ab")` обычно копирует данные (string иммутабельна) — отдельная аллокация. Компилятор оптимизирует zero-copy в частных случаях (ключ map, range, сравнение). Для явного zero-copy — `unsafe.String/Slice` (Go 1.20+). ## На что копают на senior+ - **Memory layout под нагрузкой.** Senior не просто знает про padding, а умеет посчитать `Sizeof` руками, объяснить trailing padding (выравнивание элементов массива) и оценить выигрыш на слайсе из миллионов структур через призму cache lines (64 байта) и давления на GC. - **False sharing и cache lines.** Follow-up: как padding между полями (например `_ [64]byte`) защищает от false sharing в конкурентных счётчиках; почему `sync.Pool`/atomics иногда паддят структуры. - **Атомарность 64-бит на 32-бит платформах.** На 386/arm 64-битные поля должны быть выровнены по 8 для `atomic`; до Go 1.19 это требовало ручного размещения первым полем. Senior знает про `atomic.Int64` (Go 1.19), который гарантирует выравнивание. - **`~T` в constraint'ах.** Почему `~[]int` использует именно underlying type, и как это связано с defined types в дженериках. - **Comparable в дженериках (Go 1.20).** Расширение: `comparable` теперь принимает типы вроде `any`, у которых `==` может паниковать. Follow-up — почему это сделали и какие риски. - **unsafe и эволюция API.** Знание `unsafe.Add`, `unsafe.Slice`, `unsafe.String`, `SliceData`/`StringData` вместо устаревших паттернов с `reflect.SliceHeader`; понимание правил garbage collector safety при работе с unsafe.Pointer. - **Conversion edge cases.** Срез → массив (`[N]T(s)` паникует если len < N), срез → `*[N]T`; конверсии указателей на структуры с разными тегами (Go 1.8); потеря точности float→int и поведение NaN/Inf. - **reflect и identity.** Как `reflect.Type` отличает defined type от alias, и почему `DeepEqual` ведёт себя по-разному для разных type identity.