Go não tem operador ternário. Não existe cond ? a : b. O substituto idiomático é uma instrução if:
Declare a variável com o valor padrão e depois mude-o no if. Quando os dois ramos precisam de trabalho, use if/else:
var fee int
if member {
fee = 0
} else {
fee = computeFee(order)
}
Por que Go deixou isso de fora
O FAQ do Go responde diretamente: os criadores tinham visto o operador ?: ser usado vezes demais para criar expressões impenetráveis de tão complexas, e decidiram que o if/else, embora mais longo, é inquestionavelmente mais claro. Uma linguagem precisa de uma só construção de controle de fluxo condicional.
Na prática, a falta do operador custa duas ou três linhas por uso e elimina toda uma categoria de expressões aninhadas como a ? b ? c : d : e.
Retornar cedo no lugar
Dentro de uma função, a forma mais limpa muitas vezes dispensa a variável:
Levar a condição para uma pequena função com nome costuma ser a melhor resposta: o ponto de chamada fica parecido com o ternário que você queria, e o nome da função documenta o que a condição significa.
Valores padrão com cmp.Or (Go 1.22)
O ternário mais comum em outras linguagens é "use este valor, ou um substituto se estiver vazio": name ? name : "anonymous", ou name || "anonymous" em JavaScript. O Go 1.22 trouxe o cmp.Or exatamente para isso. Ele devolve o primeiro argumento que não é o valor zero do seu tipo:
Ele trata o valor zero como "ausente", então não diferencia "a porta é 0 de propósito" de "a porta não foi definida". Quando zero é um valor válido, use um ponteiro, um bool separado ou um if explícito.
Um helper If genérico, e a sua armadilha
Com generics (Go 1.18), você pode escrever uma função que parece um ternário:
func If[T any](cond bool, a, b T) T {
if cond {
return a
}
return b
}
status := If(ok, "pass", "fail")
Funciona para valores simples, e alguns projetos usam. Mas ela não é um operador ternário, e a diferença importa. O Go avalia todos os argumentos antes de chamar uma função, então a e b são sempre calculados:
A primeira chamada calcula tanto "a" quanto "b", e a segunda causa panic de desreferência de ponteiro nil, mesmo com a condição falsa. Um ternário de verdade avaliaria só o lado escolhido. Então:
- Nunca use um helper assim quando um dos lados desreferencia um ponteiro, indexa um slice ou lê uma entrada de map que pode não existir.
- Nunca o use quando um dos lados for caro ou tiver efeitos colaterais.
- Para valores baratos e seguros funciona, mas muitos revisores de código Go ainda vão pedir o
ifsimples.
Você pode tornar os ramos preguiçosos passando funções, If(ok, func() string { return a }, func() string { return b }), mas a essa altura a instrução if é mais curta.
Outros truques, e por que evitá-los
Busca em map. map[bool]string{true: "yes", false: "no"}[ok] funciona, monta um map e calcula hashes a cada avaliação, e avalia os dois valores. É uma curiosidade, não um idioma.
Função invocada na hora. Uma função anônima chamada no próprio lugar dá uma expressão:
label := func() string {
if n > 0 {
return "positive"
}
return "non-positive"
}()
Essa sim avalia de forma preguiçosa, e às vezes ajuda dentro de um literal composto, onde não cabe uma instrução. Na maioria das vezes, calcular o valor em uma variável logo antes do literal é mais claro.
Escolhendo um substituto
| Situação | Use |
|---|---|
| Escolher entre dois valores | valor padrão, depois if |
| Os dois ramos precisam de cálculo | if/else |
| O valor decide o resultado da função | return antecipado |
| Substituto quando um valor está vazio | cmp.Or(value, fallback) |
| Três casos ou mais | switch |
| Condição repetida com um significado | uma pequena função com nome |
Propostas para acrescentar uma expressão condicional aparecem com frequência no issue tracker do Go e até agora foram recusadas. Escreva o if.
Perguntas frequentes
Go tem operador ternário?
Não. Go não tem a expressão cond ? a : b. O FAQ do Go diz que os criadores a deixaram de fora porque ela é usada com frequência demais para montar expressões difíceis de ler. Use uma instrução if/else.
Como escrever uma atribuição condicional em uma linha em Go?
Atribua o valor padrão primeiro e depois o substitua:
label := "odd"
if n%2 == 0 {
label = "even"
}
Esse é o substituto idiomático. O gofmt não coloca o corpo de um if em uma linha só, então ele sempre ocupa pelo menos três linhas.
Dá para escrever uma função ternária genérica em Go?
Sim: func If[T any](cond bool, a, b T) T { if cond { return a }; return b }. O problema é que o Go avalia todos os argumentos antes de chamar uma função, então a e b são calculados. If(p != nil, p.Name, "") ainda desreferencia um p nil e causa panic. Um ternário de verdade avaliaria só um dos lados.
Como definir um valor padrão quando uma string está vazia em Go?
Desde o Go 1.22, cmp.Or(name, "anonymous") devolve o primeiro argumento que não é o valor zero, então dá name, a menos que name seja "". Funciona com qualquer tipo comparável e, como qualquer chamada de função, avalia todos os argumentos.