Язык Go - управляющие конструкции
if, for, switch в Go — похожи на C, но с важными отличиями. Разбираем что изменилось и почему это удобнее.
Содержание
Если вы писали на C, Java или JavaScript — управляющие конструкции Go покажутся знакомыми. Но в деталях есть несколько отличий, которые сначала удивляют, а потом начинаешь ценить.
if: скобок нет, фигурные — обязательны
Базовый if выглядит так:
if x > 0 {
return y
}
Никаких скобок вокруг условия. Зато фигурные скобки обязательны всегда — даже если тело состоит из одной строки. Это не каприз, а осознанное решение: код читается одинаково, и нет классических багов типа “забыл скобки после добавления второй строки”.
Инициализация прямо в if
if умеет принимать короткое выражение перед условием:
if err := file.Chmod(0664); err != nil {
log.Print(err)
return err
}
err объявляется, сразу проверяется — и живёт только внутри этого if. Снаружи её нет. Это удобно: не засоряете область видимости переменными, которые нужны только для одной проверки.
Где же else?
В Go библиотеках вы почти не увидите else после блока, который заканчивается на return, break или continue. Вместо этого пишут так:
f, err := os.Open(name)
if err != nil {
return err
}
// продолжаем, если не было ошибки
codeUsing(f)
Логика простая: если что-то пошло не так — возвращаем результат сразу. Обработка ошибок при этом не смешивается с основной логикой — она отсекается ранними возвратами из метода.
Вот реальный паттерн работы с файлом:
f, err := os.Open(name) // открываем файл
if err != nil {
return err // если не удалось открыть — сразу выходим
}
d, err := f.Stat() // получаем информацию о файле
if err != nil {
f.Close() // закрываем файл в случае ошибки
return err
}
// основной код — работаем с файлом и его метаданными
codeUsing(f, d)
Никаких вложенных else. Код линейный и читается легко.
Небольшой сюрприз: переиспользование :=
В примере выше err объявляется дважды через := — и это не ошибка:
f, err := os.Open(name) // err объявляется здесь
d, err := f.Stat() // err переиспользуется, d — новая переменная
Правило такое: := можно использовать повторно для уже существующей переменной, если:
- Вы находитесь в той же области видимости
- Хотя бы одна переменная в левой части — новая (здесь это
d) - Тип совпадает
Это сделано специально для удобства работы с ошибками. В длинной цепочке операций не нужно придумывать err1, err2, err3 — одна переменная err переиспользуется на протяжении всей функции.
result, err := doSomething()
if err != nil { return err }
data, err := doSomethingElse(result)
// err будет перезаписана в любом случае — независимо от того, вернула функция ошибку
// или нет: даже если ошибки нет (err = nil), старое значение затирается.
if err != nil { return err }
⚠️ Осторожно с вложенными областями видимости: если написать
:=внутри блокаifилиforдля переменной из внешней области — создастся новая переменная, а не переиспользуется старая. Это один из частых источников багов у новичков.
err := doFirst()
if something {
result, err := doSecond() // ⚠️ это НОВАЯ err, внешняя не изменится
_ = result
}
// внешняя err здесь всё ещё от doFirst()
Итоги
- Скобок вокруг условий нет, фигурные — обязательны всегда
if err := ...; err != nil— стандартный паттерн, привыкайтеelseпочти не нужен: ошибки обрабатываются сразу сreturn:=можно повторять для уже объявленной переменной, если есть хотя бы одна новая
Следующий шаг: for в Go — единственный цикл в языке, который заменяет while, do-while и обычный for.