Go n'a pas d'opérateur ternaire. Il n'y a pas de cond ? a : b. Le remplacement idiomatique est une instruction if :
Déclarez la variable avec la valeur par défaut, puis modifiez-la dans le if. Quand les deux branches demandent du travail, utilisez if/else :
var fee int
if member {
fee = 0
} else {
fee = computeFee(order)
}
Pourquoi Go l'a écarté
La FAQ de Go répond directement : les concepteurs avaient trop souvent vu l'opérateur ?: servir à créer des expressions d'une complexité impénétrable, et ont décidé que if/else, bien que plus long, est incontestablement plus clair. Un langage n'a besoin que d'une seule construction de contrôle conditionnel.
En pratique, l'opérateur manquant coûte deux ou trois lignes par usage et supprime toute une catégorie d'expressions imbriquées comme a ? b ? c : d : e.
Retourner tôt à la place
Dans une fonction, la forme la plus propre se passe souvent entièrement de la variable :
Extraire la condition dans une petite fonction nommée est souvent la meilleure réponse : le site d'appel se lit comme le ternaire que vous vouliez, et le nom de la fonction documente ce que signifie la condition.
Valeurs par défaut avec cmp.Or (Go 1.22)
Le ternaire le plus courant dans d'autres langages est « utilise cette valeur, ou une valeur de repli si elle est vide » : name ? name : "anonymous", ou name || "anonymous" en JavaScript. Go 1.22 a ajouté cmp.Or exactement pour cela. Il renvoie le premier argument qui n'est pas la valeur zéro de son type :
Il traite la valeur zéro comme « manquante », donc il ne peut pas distinguer « le port vaut 0 exprès » de « le port n'a pas été défini ». Quand zéro est une valeur valide, utilisez un pointeur, un bool séparé, ou un if explicite.
Une fonction If générique, et son piège
Avec les génériques (Go 1.18), vous pouvez écrire une fonction qui ressemble à un ternaire :
func If[T any](cond bool, a, b T) T {
if cond {
return a
}
return b
}
status := If(ok, "pass", "fail")
Elle fonctionne pour des valeurs simples, et certaines bases de code l'utilisent. Mais ce n'est pas un opérateur ternaire, et la différence compte. Go évalue tous les arguments avant d'appeler une fonction, donc a et b sont toujours calculés :
Le premier appel calcule à la fois "a" et "b", et le second provoque un panic de déréférencement de pointeur nil, alors que la condition est fausse. Un vrai ternaire n'évaluerait que le côté choisi. Donc :
- N'utilisez jamais une telle fonction quand un côté déréférence un pointeur, indexe une slice ou lit une entrée de map qui peut ne pas exister.
- Ne l'utilisez jamais quand un côté est coûteux ou a des effets de bord.
- Pour des valeurs simples et sûres, elle fonctionne, mais beaucoup de relecteurs Go demanderont quand même le
ifordinaire.
Vous pouvez rendre les branches paresseuses en passant des fonctions, If(ok, func() string { return a }, func() string { return b }), mais à ce stade l'instruction if est plus courte.
Autres astuces, et pourquoi les éviter
Recherche dans une map. map[bool]string{true: "yes", false: "no"}[ok] fonctionne, construit une map et calcule un hachage à chaque évaluation, et évalue les deux valeurs. C'est une curiosité, pas un idiome.
Fonction appelée immédiatement. Une fonction anonyme appelée sur place vous donne une expression :
label := func() string {
if n > 0 {
return "positive"
}
return "non-positive"
}()
Celle-ci évalue bien de façon paresseuse, et elle aide parfois dans un littéral composite où une instruction ne peut pas se placer. La plupart du temps, calculer la valeur dans une variable juste avant le littéral est plus clair.
Choisir un remplacement
| Situation | À utiliser |
|---|---|
| Choisir entre deux valeurs | valeur par défaut, puis if |
| Les deux branches demandent un calcul | if/else |
| La valeur décide du résultat de la fonction | return anticipé |
| Valeur de repli quand une valeur est vide | cmp.Or(value, fallback) |
| Trois cas ou plus | switch |
| Condition répétée qui a un sens | une petite fonction nommée |
Des propositions d'ajouter une expression conditionnelle reviennent régulièrement dans le gestionnaire d'issues de Go et ont été refusées jusqu'ici. Écrivez le if.
Questions fréquentes
Go a-t-il un opérateur ternaire ?
Non. Go n'a pas d'expression cond ? a : b. La FAQ de Go explique que ses concepteurs l'ont écarté parce qu'il sert trop souvent à construire des expressions difficiles à lire. Utilisez plutôt une instruction if/else.
Comment écrire une affectation conditionnelle sur une ligne en Go ?
Affectez d'abord la valeur par défaut, puis remplacez-la :
label := "odd"
if n%2 == 0 {
label = "even"
}
C'est le remplacement idiomatique. gofmt ne place pas le corps d'un if sur une seule ligne, donc cela prend toujours au moins trois lignes.
Peut-on écrire une fonction ternaire générique en Go ?
Oui : func If[T any](cond bool, a, b T) T { if cond { return a }; return b }. Le hic, c'est que Go évalue tous les arguments avant d'appeler une fonction, donc a et b sont tous deux calculés. If(p != nil, p.Name, "") déréférence quand même un p nil et provoque un panic. Un vrai ternaire n'évaluerait qu'un seul côté.
Comment définir une valeur par défaut si une chaîne est vide en Go ?
Depuis Go 1.22, cmp.Or(name, "anonymous") renvoie le premier argument qui n'est pas la valeur zéro, donc il donne name sauf si name vaut "". Cela fonctionne pour tout type comparable, et comme tout appel de fonction, il évalue tous ses arguments.