· 9 мин 👁 1.1k Начинающий

Язык Go - управляющие конструкции

if, for, switch в Go — похожи на C, но с важными отличиями. Разбираем что изменилось и почему это удобнее.

ifforswitchуправляющие конструкцииошибки
Содержание

Если вы писали на 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 — новая переменная

Правило такое: := можно использовать повторно для уже существующей переменной, если:

  1. Вы находитесь в той же области видимости
  2. Хотя бы одна переменная в левой части — новая (здесь это d)
  3. Тип совпадает

Это сделано специально для удобства работы с ошибками. В длинной цепочке операций не нужно придумывать 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.