Go no tiene operador ternario. No existe cond ? a : b. El sustituto idiomático es una sentencia if:
Declara la variable con el valor por defecto y cámbiala en el if. Cuando las dos ramas necesitan trabajo, usa if/else:
var fee int
if member {
fee = 0
} else {
fee = computeFee(order)
}
Por qué Go lo dejó fuera
Las FAQ de Go lo responden directamente: los diseñadores habían visto el operador ?: usado demasiadas veces para crear expresiones complejísimas e impenetrables, y decidieron que if/else, aunque más largo, es sin duda más claro. Un lenguaje solo necesita una construcción de control de flujo condicional.
En la práctica, el operador que falta cuesta dos o tres líneas por uso y elimina toda una categoría de expresiones anidadas como a ? b ? c : d : e.
Retornar pronto
Dentro de una función, la forma más limpia a menudo se salta la variable por completo:
Llevar la condición a una pequeña función con nombre suele ser la mejor respuesta: la llamada se lee como el ternario que querías, y el nombre de la función documenta qué significa la condición.
Valores por defecto con cmp.Or (Go 1.22)
El ternario más común en otros lenguajes es "usa este valor, o uno de reserva si está vacío": name ? name : "anonymous", o name || "anonymous" en JavaScript. Go 1.22 añadió cmp.Or exactamente para eso. Devuelve el primer argumento que no es el valor cero de su tipo:
Trata el valor cero como "ausente", así que no puede distinguir "el puerto es 0 a propósito" de "el puerto no se asignó". Cuando el cero es un valor válido, usa un puntero, un bool aparte o un if explícito.
Una función genérica If, y su trampa
Con genéricos (Go 1.18) puedes escribir una función que parece un ternario:
func If[T any](cond bool, a, b T) T {
if cond {
return a
}
return b
}
status := If(ok, "pass", "fail")
Funciona con valores sencillos, y algunas bases de código la usan. Pero no es un operador ternario, y la diferencia importa. Go evalúa todos los argumentos antes de llamar a una función, así que tanto a como b se calculan siempre:
La primera llamada calcula tanto "a" como "b", y la segunda provoca un panic por desreferencia de un puntero nil, aunque la condición sea falsa. Un ternario de verdad evaluaría solo el lado elegido. Por tanto:
- Nunca uses una función así cuando uno de los lados desreferencia un puntero, indexa un slice o lee una entrada de un map que puede no existir.
- Nunca la uses cuando uno de los lados es caro o tiene efectos secundarios.
- Para valores baratos y seguros funciona, pero muchos revisores de Go te seguirán pidiendo el
ifnormal.
Puedes hacer perezosas las ramas pasando funciones, If(ok, func() string { return a }, func() string { return b }), pero a esas alturas la sentencia if es más corta.
Otros trucos, y por qué evitarlos
Búsqueda en un map. map[bool]string{true: "yes", false: "no"}[ok] funciona, construye un map y calcula un hash en cada evaluación, y evalúa los dos valores. Es una curiosidad, no un idioma.
Función invocada inmediatamente. Una función anónima llamada en el sitio te da una expresión:
label := func() string {
if n > 0 {
return "positive"
}
return "non-positive"
}()
Esto sí evalúa de forma perezosa, y a veces ayuda dentro de un literal compuesto donde no cabe una sentencia. La mayoría de las veces, calcular el valor en una variable justo antes del literal es más claro.
Elegir un sustituto
| Situación | Usa |
|---|---|
| Elegir entre dos valores | valor por defecto y luego if |
| Las dos ramas necesitan cálculos | if/else |
| El valor decide el resultado de la función | return temprano |
| Valor de reserva cuando algo está vacío | cmp.Or(value, fallback) |
| Tres o más casos | switch |
| Condición repetida con un significado | una pequeña función con nombre |
Las propuestas para añadir una expresión condicional aparecen con regularidad en el gestor de issues de Go y hasta ahora se han rechazado. Escribe el if.
Preguntas frecuentes
¿Tiene Go un operador ternario?
No. Go no tiene la expresión cond ? a : b. Las FAQ de Go dicen que los diseñadores lo dejaron fuera porque se usa demasiado a menudo para construir expresiones difíciles de leer. Usa una sentencia if/else.
¿Cómo escribo una asignación condicional en una línea en Go?
Asigna primero el valor por defecto y luego sobrescríbelo:
label := "odd"
if n%2 == 0 {
label = "even"
}
Ese es el sustituto idiomático. gofmt no pone el cuerpo de un if en una sola línea, así que siempre ocupa al menos tres líneas.
¿Puedo escribir una función ternaria genérica en Go?
Sí: func If[T any](cond bool, a, b T) T { if cond { return a }; return b }. La pega es que Go evalúa todos los argumentos antes de llamar a una función, así que se calculan tanto a como b. If(p != nil, p.Name, "") sigue desreferenciando un p nil y provoca un panic. Un ternario de verdad evaluaría solo un lado.
¿Cómo pongo un valor por defecto si un string está vacío en Go?
Desde Go 1.22, cmp.Or(name, "anonymous") devuelve el primer argumento que no es el valor cero, así que da name salvo que name sea "". Funciona con cualquier tipo comparable y, como cualquier llamada a función, evalúa todos sus argumentos.