В Go нет тернарного оператора. Никакого cond ? a : b. Идиоматичная замена это инструкция if:
Объявите переменную со значением по умолчанию, а затем измените её в if. Когда работа нужна в обеих ветках, используйте if/else:
var fee int
if member {
fee = 0
} else {
fee = computeFee(order)
}
Почему в Go его нет
Go FAQ отвечает на это прямо: создатели слишком часто видели, как оператор ?: используют для построения непроницаемо сложных выражений, и решили, что if/else, хоть и длиннее, несомненно понятнее. Языку нужна только одна конструкция условного управления.
На практике отсутствие оператора стоит двух-трёх строк на каждое использование и убирает целую категорию вложенных выражений вроде a ? b ? c : d : e.
Ранний возврат
Внутри функции самая чистая форма часто обходится вообще без переменной:
Вынести условие в маленькую именованную функцию часто лучшее решение: место вызова читается как тот самый тернарный оператор, который вам хотелось, а имя функции документирует смысл условия.
Значения по умолчанию через cmp.Or (Go 1.22)
Самый частый тернарный оператор в других языках это «используй это значение или запасное, если оно пустое»: name ? name : "anonymous" или name || "anonymous" в JavaScript. Ровно для этого в Go 1.22 появился cmp.Or. Он возвращает первый аргумент, который не равен нулевому значению своего типа:
Нулевое значение он считает «отсутствующим», поэтому не отличает «порт равен 0 намеренно» от «порт не задан». Когда ноль допустимое значение, используйте указатель, отдельный bool или явный if.
Обобщённый помощник If и его ловушка
С дженериками (Go 1.18) можно написать функцию, которая выглядит как тернарный оператор:
func If[T any](cond bool, a, b T) T {
if cond {
return a
}
return b
}
status := If(ok, "pass", "fail")
Для простых значений она работает, и в некоторых кодовых базах её используют. Но это не тернарный оператор, и разница важна. Go вычисляет все аргументы до вызова функции, поэтому a и b вычисляются всегда:
Первый вызов вычисляет и "a", и "b", а второй паникует с разыменованием nil-указателя, хотя условие ложно. Настоящий тернарный оператор вычислил бы только выбранную сторону. Поэтому:
- Никогда не используйте такой помощник, когда одна из сторон разыменовывает указатель, обращается к слайсу по индексу или читает элемент мапы, которого может не быть.
- Никогда не используйте его, когда одна из сторон дорогая или имеет побочные эффекты.
- Для дешёвых и безопасных значений он работает, но многие ревьюеры Go всё равно попросят обычный
if.
Ветки можно сделать ленивыми, передавая функции, If(ok, func() string { return a }, func() string { return b }), но тогда инструкция if уже короче.
Другие трюки и почему их лучше избегать
Поиск в мапе. map[bool]string{true: "yes", false: "no"}[ok] работает, при каждом вычислении строит мапу и хеширует ключ, а также вычисляет оба значения. Это курьёз, а не идиома.
Немедленно вызываемая функция. Анонимная функция, вызванная на месте, даёт выражение:
label := func() string {
if n > 0 {
return "positive"
}
return "non-positive"
}()
Здесь вычисление действительно ленивое, и иногда это помогает внутри составного литерала, куда инструкцию не поставить. Но чаще понятнее вычислить значение в переменную прямо перед литералом.
Чем заменить
| Ситуация | Что использовать |
|---|---|
| Выбор между двумя значениями | значение по умолчанию, затем if |
| Обе ветки требуют вычислений | if/else |
| Значение определяет результат функции | ранний return |
| Запасное значение, если значение пустое | cmp.Or(value, fallback) |
| Три и больше вариантов | switch |
| Повторяющееся условие со смыслом | маленькая именованная функция |
Предложения добавить условное выражение регулярно появляются в трекере задач Go и пока отклоняются. Пишите if.
Часто задаваемые вопросы
Есть ли в Go тернарный оператор?
Нет. Выражения cond ? a : b в Go нет. В Go FAQ сказано, что создатели не включили его, потому что им слишком часто строят трудночитаемые выражения. Используйте инструкцию if/else.
Как написать условное присваивание в одну строку в Go?
Сначала присвойте значение по умолчанию, затем переопределите его:
label := "odd"
if n%2 == 0 {
label = "even"
}
Это и есть идиоматичная замена. gofmt не оставит тело if на одной строке, так что это всегда минимум три строки.
Можно ли написать обобщённую тернарную функцию в Go?
Да: func If[T any](cond bool, a, b T) T { if cond { return a }; return b }. Подвох в том, что Go вычисляет все аргументы до вызова функции, поэтому вычисляются и a, и b. If(p != nil, p.Name, "") всё равно разыменует nil p и запаникует. Настоящий тернарный оператор вычислил бы только одну сторону.
Как задать значение по умолчанию, если строка пустая, в Go?
Начиная с Go 1.22 cmp.Or(name, "anonymous") возвращает первый аргумент, не равный нулевому значению, то есть даёт name, если только name не равно "". Функция работает с любым сравнимым типом и, как любой вызов функции, вычисляет все свои аргументы.