Go hat keinen ternären Operator. Es gibt kein cond ? a : b. Der idiomatische Ersatz ist eine if-Anweisung:
Deklarier die Variable mit dem Standardwert und änder sie dann im if. Wenn beide Zweige Arbeit machen, nimm if/else:
var fee int
if member {
fee = 0
} else {
fee = computeFee(order)
}
Warum Go ihn weggelassen hat
Die Go-FAQ beantwortet das direkt: Die Designer hatten zu oft gesehen, wie der ?:-Operator für undurchdringlich komplexe Ausdrücke benutzt wurde, und entschieden, dass if/else zwar länger, aber eindeutig klarer ist. Eine Sprache braucht nur ein Konstrukt für bedingten Kontrollfluss.
In der Praxis kostet der fehlende Operator zwei oder drei Zeilen pro Verwendung und beseitigt eine ganze Kategorie verschachtelter Ausdrücke wie a ? b ? c : d : e.
Stattdessen früh zurückkehren
Innerhalb einer Funktion kommt die sauberste Form oft ganz ohne Variable aus:
Die Bedingung in eine kleine benannte Funktion auszulagern ist oft die beste Lösung: Die Aufrufstelle liest sich wie der Ternary, den du wolltest, und der Funktionsname dokumentiert, was die Bedingung bedeutet.
Standardwerte mit cmp.Or (Go 1.22)
Der häufigste Ternary in anderen Sprachen ist „nimm diesen Wert oder einen Fallback, wenn er leer ist“: name ? name : "anonymous", oder name || "anonymous" in JavaScript. Go 1.22 hat genau dafür cmp.Or eingeführt. Es gibt das erste Argument zurück, das nicht der Nullwert seines Typs ist:
Es behandelt den Nullwert als „fehlt“ und kann daher „Port ist absichtlich 0“ nicht von „Port wurde nicht gesetzt“ unterscheiden. Wenn null ein gültiger Wert ist, nimm einen Pointer, ein separates bool oder ein explizites if.
Ein generischer If-Helfer und seine Falle
Mit Generics (Go 1.18) kannst du eine Funktion schreiben, die wie ein Ternary aussieht:
func If[T any](cond bool, a, b T) T {
if cond {
return a
}
return b
}
status := If(ok, "pass", "fail")
Für einfache Werte funktioniert das, und manche Codebasen nutzen es. Aber es ist kein ternärer Operator, und der Unterschied zählt. Go wertet alle Argumente aus, bevor eine Funktion aufgerufen wird, also werden a und b immer beide berechnet:
Der erste Aufruf berechnet sowohl "a" als auch "b", und der zweite löst eine Panic wegen Nil-Pointer-Dereferenzierung aus, obwohl die Bedingung falsch ist. Ein echter Ternary würde nur die gewählte Seite auswerten. Also:
- Nutz so einen Helfer nie, wenn eine Seite einen Pointer dereferenziert, einen Slice indiziert oder einen Map-Eintrag liest, der vielleicht nicht existiert.
- Nutz ihn nie, wenn eine Seite teuer ist oder Seiteneffekte hat.
- Für billige, sichere Werte funktioniert er, aber viele Go-Reviewer werden trotzdem nach dem schlichten
iffragen.
Du kannst die Zweige lazy machen, indem du Funktionen übergibst, If(ok, func() string { return a }, func() string { return b }), aber dann ist die if-Anweisung kürzer.
Andere Tricks und warum du sie meiden solltest
Map-Lookup. map[bool]string{true: "yes", false: "no"}[ok] funktioniert, baut und hasht bei jeder Auswertung eine Map und wertet beide Werte aus. Eine Kuriosität, kein Idiom.
Sofort aufgerufene Funktion. Eine anonyme Funktion, die an Ort und Stelle aufgerufen wird, ergibt einen Ausdruck:
label := func() string {
if n > 0 {
return "positive"
}
return "non-positive"
}()
Das wertet tatsächlich lazy aus und hilft gelegentlich innerhalb eines Composite Literals, wo keine Anweisung stehen kann. Meistens ist es klarer, den Wert direkt vor dem Literal in einer Variablen zu berechnen.
Den passenden Ersatz wählen
| Situation | Nimm |
|---|---|
| Zwischen zwei Werten wählen | Standardwert, dann if |
| Beide Zweige brauchen Berechnung | if/else |
| Der Wert bestimmt das Ergebnis der Funktion | frühes return |
| Fallback, wenn ein Wert leer ist | cmp.Or(value, fallback) |
| Drei oder mehr Fälle | switch |
| Wiederkehrende Bedingung mit Bedeutung | eine kleine benannte Funktion |
Vorschläge für einen bedingten Ausdruck tauchen regelmäßig im Issue-Tracker von Go auf und wurden bisher abgelehnt. Schreib das if.
Häufig gestellte Fragen
Hat Go einen ternären Operator?
Nein. Go hat keinen Ausdruck cond ? a : b. Laut Go-FAQ haben die Designer ihn weggelassen, weil er zu oft für schwer lesbare Ausdrücke benutzt wird. Nimm stattdessen eine if/else-Anweisung.
Wie schreibe ich eine einzeilige bedingte Zuweisung in Go?
Weise zuerst den Standardwert zu und überschreib ihn dann:
label := "odd"
if n%2 == 0 {
label = "even"
}
Das ist der idiomatische Ersatz. gofmt setzt einen if-Rumpf nie in eine Zeile, also sind es immer mindestens drei Zeilen.
Kann ich in Go eine generische Ternary-Funktion schreiben?
Ja: func If[T any](cond bool, a, b T) T { if cond { return a }; return b }. Der Haken: Go wertet jedes Argument aus, bevor eine Funktion aufgerufen wird, also werden a und b beide berechnet. If(p != nil, p.Name, "") dereferenziert ein nil-p trotzdem und löst eine Panic aus. Ein echter Ternary würde nur eine Seite auswerten.
Wie setze ich in Go einen Standardwert, wenn ein String leer ist?
Seit Go 1.22 gibt cmp.Or(name, "anonymous") das erste Argument zurück, das nicht der Nullwert ist, also liefert es name, außer name ist "". Es funktioniert mit jedem vergleichbaren Typ und wertet wie jeder Funktionsaufruf alle seine Argumente aus.