Menu

Operatore ternario in Golang: perché Go non lo ha e cosa usare

Go non ha un operatore ternario (cond ? a : b). Questa pagina spiega perché, mostra il sostituto idiomatico con if/else, cmp.Or per i valori di default e una funzione generica di supporto, insieme alla trappola che la rende diversa da un vero ternario.

Questa pagina include editor eseguibili: modifica, esegui e vedi subito l'output.

Go non ha un operatore ternario. Non esiste cond ? a : b. Il sostituto idiomatico è un'istruzione if:

Dichiara la variabile con il valore di default, poi cambiala nell'if. Quando entrambi i rami devono fare qualcosa, usa if/else:

var fee int
if member {
	fee = 0
} else {
	fee = computeFee(order)
}

Perché Go l'ha lasciato fuori

Le FAQ di Go rispondono direttamente: i progettisti avevano visto l'operatore ?: usato troppo spesso per creare espressioni impenetrabili e complicate, e hanno deciso che if/else, pur essendo più lungo, è senza dubbio più chiaro. A un linguaggio basta un solo costrutto condizionale per il controllo del flusso.

In pratica l'operatore mancante costa due o tre righe per ogni uso ed elimina un'intera categoria di espressioni annidate come a ? b ? c : d : e.

Ritornare subito

Dentro una funzione, la forma più pulita spesso fa a meno della variabile:

Spostare la condizione in una piccola funzione con un nome è spesso la risposta migliore: la chiamata si legge come il ternario che volevi, e il nome della funzione documenta cosa significa la condizione.

Valori di default con cmp.Or (Go 1.22)

Il ternario più comune negli altri linguaggi è "usa questo valore, oppure uno di riserva se è vuoto": name ? name : "anonymous", oppure name || "anonymous" in JavaScript. Go 1.22 ha aggiunto cmp.Or proprio per questo. Restituisce il primo argomento che non è il valore zero del suo tipo:

Tratta il valore zero come "assente", quindi non può distinguere "la porta è 0 di proposito" da "la porta non è stata impostata". Quando lo zero è un valore valido, usa un puntatore, un bool separato o un if esplicito.

Una funzione generica If, e la sua trappola

Con i generics (Go 1.18) puoi scrivere una funzione che sembra un ternario:

func If[T any](cond bool, a, b T) T {
	if cond {
		return a
	}
	return b
}

status := If(ok, "pass", "fail")

Funziona con valori semplici, e alcune codebase la usano. Ma non è un operatore ternario, e la differenza conta. Go valuta tutti gli argomenti prima di chiamare una funzione, quindi sia a sia b vengono sempre calcolati:

La prima chiamata calcola sia "a" sia "b", e la seconda va in panic con una dereferenziazione di puntatore nil, anche se la condizione è falsa. Un vero ternario valuterebbe solo il lato scelto. Quindi:

  • Non usare mai una funzione del genere quando uno dei lati dereferenzia un puntatore, indicizza uno slice o legge una voce di una mappa che potrebbe non esistere.
  • Non usarla mai quando uno dei lati è costoso o ha effetti collaterali.
  • Per valori economici e sicuri funziona, ma molti revisori Go ti chiederanno comunque il semplice if.

Puoi rendere pigri i rami passando delle funzioni, If(ok, func() string { return a }, func() string { return b }), ma a quel punto l'istruzione if è più corta.

Altri trucchi, e perché evitarli

Ricerca in una mappa. map[bool]string{true: "yes", false: "no"}[ok] funziona, costruisce una mappa e calcola un hash a ogni valutazione, e valuta entrambi i valori. È una curiosità, non un idioma.

Funzione invocata immediatamente. Una funzione anonima chiamata sul posto ti dà un'espressione:

label := func() string {
	if n > 0 {
		return "positive"
	}
	return "non-positive"
}()

Questa valuta davvero in modo pigro, e a volte aiuta dentro un letterale composito dove non si può mettere un'istruzione. Nella maggior parte dei casi, calcolare il valore in una variabile subito prima del letterale è più chiaro.

Scegliere un sostituto

SituazioneUsa
Scegliere tra due valorivalore di default, poi if
Entrambi i rami richiedono calcoliif/else
Il valore decide il risultato della funzionereturn anticipato
Valore di riserva quando qualcosa è vuotocmp.Or(value, fallback)
Tre o più casiswitch
Condizione ripetuta con un significatouna piccola funzione con un nome

Le proposte di aggiungere un'espressione condizionale compaiono regolarmente nell'issue tracker di Go e finora sono state respinte. Scrivi l'if.

Domande frequenti

Go ha un operatore ternario?

No. Go non ha l'espressione cond ? a : b. Le FAQ di Go spiegano che i progettisti l'hanno lasciata fuori perché viene usata troppo spesso per costruire espressioni difficili da leggere. Usa invece un'istruzione if/else.

Come scrivo un'assegnazione condizionale su una riga in Go?

Assegna prima il valore di default, poi sovrascrivilo:

label := "odd"
if n%2 == 0 {
	label = "even"
}

Questo è il sostituto idiomatico. gofmt non mette il corpo di un if su una sola riga, quindi occupa sempre almeno tre righe.

Posso scrivere una funzione ternaria generica in Go?

Sì: func If[T any](cond bool, a, b T) T { if cond { return a }; return b }. Il problema è che Go valuta tutti gli argomenti prima di chiamare una funzione, quindi vengono calcolati sia a sia b. If(p != nil, p.Name, "") dereferenzia comunque un p nil e va in panic. Un vero ternario valuterebbe un solo lato.

Come imposto un valore di default se una stringa è vuota in Go?

Da Go 1.22, cmp.Or(name, "anonymous") restituisce il primo argomento che non è il valore zero, quindi dà name a meno che name non sia "". Funziona con qualsiasi tipo confrontabile e, come ogni chiamata di funzione, valuta tutti i suoi argomenti.

Illustrazione dei linguaggi di programmazione di Coddy

Impara a programmare con Coddy

INIZIA