Menu

Opérateur ternaire en Golang : pourquoi Go n'en a pas et quoi utiliser

Go n'a pas d'opérateur ternaire (cond ? a : b). Cette page explique pourquoi, montre le remplacement idiomatique par if/else, cmp.Or pour les valeurs par défaut, et une fonction utilitaire générique avec le piège qui la distingue d'un vrai ternaire.

Cette page contient des éditeurs exécutables - modifiez, exécutez et voyez la sortie instantanément.

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

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 valeursvaleur par défaut, puis if
Les deux branches demandent un calculif/else
La valeur décide du résultat de la fonctionreturn anticipé
Valeur de repli quand une valeur est videcmp.Or(value, fallback)
Trois cas ou plusswitch
Condition répétée qui a un sensune 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.

Coddy programming languages illustration

Apprendre à coder avec Coddy

COMMENCER