Значения, константы и числа
Go намеренно сделал базовые значения маленькими и предсказуемыми — горстка целых типов фиксированной ширины, строки только для чтения, константы со счётчиком iota. Из-за этой простоты их почти не учат всерьёз: «ну число и число, чего там разбираться». Именно поэтому они дают самые тихие и дорогие баги — те, что не падают при сборке и всплывают только на проде или на собеседовании.
Под простым синтаксисом прячется механика. Целое — это фиксированная раскладка бит с конкретным поведением при переполнении, а ширина int зависит от платформы и фиксируется компилятором, а не выбирается в рантайме. Беззнаковый тип не уходит в минус — он оборачивается по модулю 2^n. Строка — это неизменяемая последовательность байтов, и len(s) считает именно байты, а не символы. Эта тема разбирает значения по слоям — от устройства одного целого до правил видимости имён в пакете.
Карта темы
- Знаковые и беззнаковые типы — дополнительный код, неотрицательные
uintи оборачивание по модулю 2^n при переполнении вместо ошибки. - Размеры целых типов — фиксированные ширины 8/16/32/64 бита и платформозависимый
int, ширина которого задаётся реализацией, а не рантаймом. - Диапазон целых типов — как дополнительный код делит биты между отрицательной и положительной частью и почему максимум
int64равен 2^63−1. - Целочисленное деление —
/целых усекает к нулю, конверсию делают до деления, а деление на ноль паникует. - Неизменяемость строк —
stringтолько для чтения,s[0] = ...не компилируется, правка байта идёт через[]byteс копией. - Руны и байты —
len(s)считает байты,utf8.RuneCountInString— руны, аrangeпо строке декодирует руны. - iota и константы — счётчик
iotaрастёт построчно вconst-блоке (учитывая_) и обнуляется в новом блоке. - Области видимости и экспорт — вложенные области от блока до universe и видимость между пакетами по регистру имени.
- switch и управление потоком —
switchбез проваливания,fallthrough,switch true, type switch и labeled break в цикле. - Структуры — композитный тип из именованных полей, value-семантика с копированием, встраивание, методы и теги.
Частые ошибки и ловушки
| Ошибка | Последствие |
|---|---|
Считать, что uint при переполнении паникует или насыщается | Тихое оборачивание по модулю 2^n; бесконечный цикл или неверный счётчик |
Вычитать больший uint из меньшего, ожидая отрицательного результата | Получается огромное положительное число — нет промежуточного «минуса» |
| Думать, что знаковый тип хранит знак отдельным флаг-битом | Неверная модель дополнительного кода; ошибка в расчёте диапазона |
Считать int всегда 32-битным или всегда 64-битным | Несовпадение размеров между платформами; битый бинарный формат |
Брать максимум int64 как 2^64−1 или 2^63 | Это диапазон uint64 или ошибка на единицу; верно 2^63−1 |
Писать float64(a / b) вместо float64(a) / float64(b) | Деление усекает в int ДО приведения — точность теряется |
| Делить на ноль без проверки длины среза | sum / len(s) на пустом срезе паникует integer divide by zero |
Считать, что s[0] = 'R' — это runtime-паника | Это ошибка компиляции — string не поддерживает запись по индексу |
Считать len(s) числом символов строки | len считает байты; многобайтовый символ UTF-8 считается дважды и больше |
Забыть, что строка с _ тоже потребляет значение iota | Сдвиг всех последующих констант на единицу |
Путать := во вложенном блоке с присваиванием | Заводит НОВУЮ переменную, затеняя внешнюю — ошибка или значение «теряются» |
Ждать, что break в switch внутри for выйдет из цикла | Выходит только из switch; цикл продолжает крутиться |
| Считать копию структуры ссылкой | b := a копирует ВСЕ поля; правка b не видна в a |
| Принимать встраивание структуры за наследование | Это композиция: нет полиморфизма, своё поле просто затеняет встроенное |
Значение для собеседований
Числа и значения — тёплая часть Go-интервью, но именно на ней отсеивают невнимательных. Спрашивают не синтаксис, а модель поведения под ним: когда тип оборачивается, когда деление усекает, что именно считает len.
Что обычно проверяют:
- Разница знаковых и беззнаковых типов — дополнительный код и оборачивание по модулю 2^n.
- Какие ширины целых даёт Go и что определяет ширину
int— реализация и платформа, не рантайм. - Как вычислить диапазон
int64и почему его максимум — 2^63−1, а не 2^63 и не 2^64−1. - Что делает целочисленное
/и почемуfloat64(a / b)теряет точность. - Почему
stringнеизменяема и что возвращает индексирование строки —byte, а не символ. - Чем
len(s)отличается от числа рун и какrangeпо строке декодирует руны. - Как считает
iota(включая строки с_) и какие правила видимости и затенения имён в пакете.
Типичный неверный ответ: «беззнаковый счётчик при вычитании уйдёт в минус — это же обычная арифметика». Это запускает разбор того, что у uint нет «минуса»: при переполнении он оборачивается по модулю 2^n, и цикл for i := uint(...); i >= 0; i-- никогда не завершается.