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
| Situazione | Usa |
|---|---|
| Scegliere tra due valori | valore di default, poi if |
| Entrambi i rami richiedono calcoli | if/else |
| Il valore decide il risultato della funzione | return anticipato |
| Valore di riserva quando qualcosa è vuoto | cmp.Or(value, fallback) |
| Tre o più casi | switch |
| Condizione ripetuta con un significato | una 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.