Основы и пакеты
Как устроена программа на Go — пакеты и импорты, объявления переменных и констант, базовые типы, нулевые значения, iota, видимость имён и инструменты сборки.
9 вопросов
JuniorТеорияОчень частоКаковы нулевые значения базовых типов Go?
Каковы нулевые значения базовых типов Go?
Каждая переменная, объявленная без инициализатора, получает нулевое значение своего типа, поэтому в Go нет неинициализированного мусора. Числа — 0, bool — false, а string — "" (пустая, не nil). Указатели, slice, map, channel, func и interface равны nil. Нулевое значение struct имеет все поля обнулёнными, а у массива каждый элемент тоже обнулён.
Типичные ошибки
- ✗Думать, что неинициализированная переменная держит мусор — Go всегда её обнуляет
- ✗Считать, что нулевая
string— это nil, а не пустая строка"" - ✗Забывать, что slice, map, channel и interface обнуляются в
nil
Уточняющие вопросы
- →Что происходит при записи в nil map против чтения из неё?
- →Почему в nil slice всё ещё можно безопасно делать append?
JuniorТеорияЧастоКакие в Go базовые типы, и какие правила действуют при преобразовании между ними?
Какие в Go базовые типы, и какие правила действуют при преобразовании между ними?
Базовые типы Go — это целые фиксированной ширины (int, int8..int64, семейство uint), числа с плавающей точкой (float32, float64), bool и string, причём byte — псевдоним для uint8, а rune — для int32. Неявного числового преобразования нет: преобразовывать нужно явно, например float64(x), даже между int и int64.
Типичные ошибки
- ✗Ждать, что
intсам расширится вfloat64без приведения - ✗Думать, что
byteиrune— самостоятельные типы, а не псевдонимы дляuint8/int32 - ✗Считать
intиint64одним типом, не требующим преобразования
Уточняющие вопросы
- →Что может пойти не так при преобразовании
float64вintв Go? - →Как размер
intзависит от целевой платформы?
JuniorТеорияЧастоЧто делают команды go run, go build и go mod в Go?
Что делают команды go run, go build и go mod в Go?
go run компилирует программу во временное место и сразу запускает её, не оставляя бинарника. go build компилирует пакет в один самодостаточный исполняемый файл на диске, но не запускает его. go mod управляет модулем и его зависимостями, имея подкоманды вроде go mod init и go mod tidy.
Типичные ошибки
- ✗Думать, что
go runоставляет бинарник на диске, какgo build - ✗Считать, что исполняемому файлу от
go buildнужна установленная среда Go - ✗Путать
go mod(управление зависимостями) с простым загрузчиком пакетов
Уточняющие вопросы
- →В чём разница между
go mod initиgo mod tidy? - →Как кросс-скомпилировать бинарник
go buildпод другую ОС или архитектуру?
JuniorТеорияЧастоЧем различаются var, const и := при объявлении значений в Go?
Чем различаются var, const и := при объявлении значений в Go?
var объявляет переменную с явным типом или выведенным из инициализатора. const объявляет неизменяемое значение, зафиксированное на этапе компиляции. := — это короткое объявление с выводом типа, допустимое только внутри функции. На уровне пакета можно использовать лишь var и const; := — это форма внутри тела функции.
Типичные ошибки
- ✗Думать, что
:=работает на уровне пакета — он допустим лишь внутри функции - ✗Считать, что
constможно присвоить значение, вычисленное во время выполнения - ✗Забывать, что
varможет опустить тип и вывести его из инициализатора
Уточняющие вопросы
- →В чём разница между типизированной и нетипизированной константой в Go?
- →Когда
:=переиспользует существующую переменную, а когда объявляет новую?
JuniorТеорияЧастоЧто такое пакет в Go, и что делают package main и import?
Что такое пакет в Go, и что делают package main и import?
Пакет объединяет файлы .go из одной директории, которые компилируются вместе и делят свои идентификаторы. package main вместе с func main() обозначает точку входа исполняемой программы. import подключает другой пакет по его import-пути, чтобы вызывать его экспортированные имена. Неиспользованный импорт — это ошибка компиляции, а не предупреждение.
Типичные ошибки
- ✗Думать, что каждый файл — отдельный пакет, а не вся директория как один пакет
- ✗Считать неиспользованный импорт лишь предупреждением — он валит сборку
- ✗Забывать, что именно
package mainвместе сfunc main()даёт исполняемый файл
Уточняющие вопросы
- →Чем import-путь отличается от имени пакета, по которому к нему обращаются?
- →Что делает пустой импорт
import _ "pkg"и когда он применяется?
JuniorТеорияЧастоКак Go решает, экспортирован ли идентификатор из пакета?
Как Go решает, экспортирован ли идентификатор из пакета?
Видимость определяется исключительно первой буквой имени. Идентификатор, у которого первая буква заглавная, экспортирован и виден из других пакетов; строчная первая буква делает его приватным для пакета. Правило одинаково применяется к функциям, типам, переменным, константам, полям struct и методам — ключевых слов public/private нет.
Типичные ошибки
- ✗Думать, что в Go есть
public/privateвместо правила о регистре - ✗Считать, что поля struct всегда видимы независимо от первой буквы
- ✗Полагать, что правило касается лишь функций, а не полей, методов или констант
Уточняющие вопросы
- →Может ли экспортированный struct иметь неэкспортированные поля, и что это значит?
- →Как пакеты
internal/дополнительно ограничивают, кто может их импортировать?
JuniorКодИногдаКакие значения получают константы от iota в этих двух блоках const?
Какие значения получают константы от iota в этих двух блоках const?
A=0, B=1, D=3 — iota начинается с 0 в каждом блоке const и растёт на единицу с каждой строкой, включая пропущенную строку _ (она занимает значение 2). Во втором блоке KB = 1 << (10 * (iota + 1)) даёт KB=1024 и MB=1048576, потому что iota равен 0, затем 1 на следующих строках.
Типичные ошибки
- ✗Думать, что пропущенная строка
_не продвигаетiota— она всё равно занимает значение - ✗Считать
iotaглобальным счётчиком, а не сбрасываемым в каждом блокеconst - ✗Забывать, что
iotaвычисляется внутри полного выражения строки, например1 << (10*(iota+1))
Уточняющие вопросы
- →Как определить
KB, MB, GBкак степени двойки одним выражением сiota? - →Что произойдёт с
iota, если две константы делят одну строку черезA, B = iota, iota?
MiddleТеорияИногдаКак типизированные константы и iota строят enum в Go, и чем они отличаются от untyped-констант?
Как типизированные константы и iota строят enum в Go, и чем они отличаются от untyped-констант?
Блок const с iota генерирует последовательные значения по строкам. Если задать тип первой константе (type Weekday int; Sunday Weekday = iota), весь столбец становится этим типизированным enum. Подвох: untyped-константа неявно конвертируется в нужный контексту тип, а ТИПИЗИРОВАННАЯ — нет. Поэтому смешать значение Weekday с обычным int — ошибка компиляции; нужна явная int(Sunday).
Типичные ошибки
- ✗Думать, что именованный enum-тип и обычный
intвзаимозаменяемы без явной конверсии - ✗Забывать, что
iotaсбрасывается на блокconst, а не на строку, и растёт каждую строку - ✗Путать неявную конверсию untyped-константы со строгостью типизированной
Уточняющие вопросы
- →Как дать enum читаемые имена, например через метод
String()? - →Что задаёт тип по умолчанию untyped-константы и когда он применяется?
MiddleТеорияРедкоПочему := во вложенной области может молча затенить внешнюю переменную?
Почему := во вложенной области может молча затенить внешнюю переменную?
:= всегда объявляет НОВЫЕ переменные в текущем блоке. Если слева стоит имя из внешней области, вложенный := создаёт свежую внутреннюю переменную, которая затеняет — а не переприсваивает — внешнюю, поэтому запись через неё не достигает внешней. Свежее затенение стартует с нулевого значения — классический баг с err. Используйте =, чтобы присвоить, или объявите имя один раз и переиспользуйте.
Типичные ошибки
- ✗Считать, что вложенный
:=по внешнему имени переприсваивает его, а не объявляет затенение - ✗Использовать
:=внутри блокаif/forи потом читать устаревший внешнийerr - ✗Думать, что затенение наследует внешнее значение, а не стартует с нулевого
Уточняющие вопросы
- →Какие инструменты Go или проверки vet ловят случайное затенение?
- →Когда
:=законно переиспользует внешнее имя, не затеняя его?