Тернарный оператор в Go: как заменить C и не сойти с ума

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Введение: тернарный оператор — друг или враг?

Если вы пришли в Go из мира C, C++, Java или JavaScript, вы наверняка привыкли к лаконичной конструкции:

int max = (a > b) ? a : b;

Это — тернарный оператор. Он компактный, элегантный и позволяет записать простое ветвление в одну строку. Но когда вы начинаете писать на Go, вас ждёт сюрприз: в Go нет тернарного оператора. Вообще. Никакого ? :.

Почему создатели языка пошли на такой шаг? Как теперь писать короткие условные присваивания? И главное — как это делать идиоматично, то есть в стиле, который принят в сообществе Go?

В этой статье мы разберём:

  • Почему в Go нет тернарного оператора (спойлер: это осознанное решение).
  • Как заменить тернарник с помощью if-else.
  • Продвинутые техники: функции, map, inlining.
  • Когда можно (и нужно) нарушать правило.

Поехали!

Почему Go отказался от тернарного оператора?

Ответ прост и лежит в философии языка: читаемость важнее краткости.

Роберт Гриземер (один из создателей Go) однажды сказал: «Тернарный оператор — это источник багов. Он слишком легко читается неправильно, особенно когда вложен». И это правда. Вспомните хотя бы знаменитый пример из C:

int x = (a ? b : c) ? d : e;

Попробуйте сходу понять, что он делает? Вот и создатели Go решили, что лучше пожертвовать одной строкой, но сделать код очевидным.

В Go приоритет — ясность намерений. Если вы пишете условие, пусть оно будет явным. Тернарный оператор часто превращается в «магическое число»: вы смотрите на строку и не сразу понимаете логику. В Go такого быть не должно.

Базовая замена: if-else как идиома

Самый простой и правильный способ заменить тернарник — использовать обычный if-else. Да, это занимает 3 строки вместо 1, но зато читается без напряга.

Пример: найти максимум двух чисел.

На C:

int max = (a > b) ? a : b;

На Go (идиоматично):

var max int
if a > b {
    max = a
} else {
    max = b
}

«Но это же длиннее!» — скажете вы. Да. Но взгляните на это с другой стороны: через месяц вы откроете код и сразу увидите логику. Никаких скобок, никаких двусмысленностей. Просто «если a больше b, то max = a, иначе max = b».

Более того, Go-сообщество считает такой стиль единственно правильным для простых условий. Не пытайтесь «схитрить» и запихнуть всё в одну строку — это будет считаться плохим тоном.

Продвинутые техники: когда хочется краткости

Иногда вы пишете код, где if-else занимает 10 строк, хотя по сути — это простое присваивание. В таких случаях можно применить несколько шаблонов, которые сохраняют идиоматичность, но делают код компактнее.

1. Вспомогательная функция

Если вам часто нужно выбирать между двумя значениями, вынесите логику в функцию. Это особенно удобно для однострочных операций.

func ternary(cond bool, a, b string) string {
    if cond {
        return a
    }
    return b
}

// Использование:
greeting := ternary(isMorning, "Доброе утро", "Добрый вечер")

Важно: не называйте функцию ternary — это считается дурным тоном, так как вы пытаетесь «протащить» тернарник через чёрный ход. Лучше дать осмысленное имя, например ifElse или choose. Но если вы пишете для себя — почему бы и нет?

2. Использование map для выбора

Если условие сводится к выбору из двух вариантов, можно использовать map с булевыми ключами. Это выглядит необычно, но иногда читается даже лучше.

var result = map[bool]string{
    true:  "positive",
    false: "negative",
}[value > 0]

Минус: вы создаёте map на каждый вызов (если не оптимизировать), что может быть дорого. Плюс: очень кратко. Используйте только в критических местах, где скорость не важна, а читаемость — да.

3. Inline-выражение с функцией (анонимная)

Можно создать анонимную функцию прямо на месте. Это редкость, но иногда оправдано в тестах или конфигах.

var status = func() string {
    if isActive {
        return "active"
    }
    return "inactive"
}()

Это всё ещё if-else, но упакованный в одну строку. Однако помните: такая конструкция может сбить с толку новичков. Используйте с осторожностью.

Реальные примеры из практики

Давайте посмотрим, как отсутствие тернарника влияет на реальный код. Вот несколько типичных ситуаций.

Пример 1: Выбор строки в зависимости от булева флага

Плохо (попытка имитировать тернарник):

msg := "Hello"
if isFormal {
    msg = "Greetings"
}

Этот код работает, но он неочевиден: вы присваиваете значение, а потом перезаписываете. Лучше:

var msg string
if isFormal {
    msg = "Greetings"
} else {
    msg = "Hello"
}

Или ещё лучше — сразу присвоить значение по умолчанию, а потом перезаписать, если нужно. Но это уже дело вкуса.

Пример 2: Вычисление скидки

Представьте, что нужно посчитать цену со скидкой:

// C:
float price = isPremium ? basePrice * 0.9 : basePrice;

// Go:
var price float64
if isPremium {
    price = basePrice * 0.9
} else {
    price = basePrice
}

Кода больше, но зато сразу видно: «если премиум, то скидка 10%, иначе полная цена».

Пример 3: Проверка ошибок (очень частый паттерн в Go)

В Go вы постоянно проверяете ошибки. Тернарник тут был бы особенно вреден, потому что он скрывает логику обработки ошибок.

// Плохая идея (если бы тернарник был):
result, err := doSomething()
if err != nil {
    return "", err
}                

Блоги

Рекомендаций по книгам