Menu

Operador ternário em Golang: por que Go não tem e o que usar

Go não tem operador ternário (cond ? a : b). Esta página explica o porquê, mostra o substituto idiomático com if/else, o cmp.Or para valores padrão e uma função genérica auxiliar, junto com a armadilha que a diferencia de um ternário de verdade.

Esta página tem editores executáveis - edite, execute e veja a saída na hora.

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 if simples.

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çãoUse
Escolher entre dois valoresvalor padrão, depois if
Os dois ramos precisam de cálculoif/else
O valor decide o resultado da funçãoreturn antecipado
Substituto quando um valor está vaziocmp.Or(value, fallback)
Três casos ou maisswitch
Condição repetida com um significadouma 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.

Coddy programming languages illustration

Aprenda a programar com o Coddy

COMEÇAR