Missing in Go, Part 1 - The Ternary Operator
This is a personal list of features I keep reaching for in Go, only to remember they don’t exist. I update it as I go.
Ternary operator
Every time I need a quick conditional assignment, instinct kicks in and I write:
n := a < b ? a : bAnd it still doesn’t compile.
It’s not a dealbreaker, but it gets me every time.
The official Go FAQ justifies the decision like this:
“The reason ?: is absent from Go is that the language’s designers had seen the operation used too often to create impenetrably complex expressions. The if-else form, although longer, is unquestionably clearer. A language needs only one conditional control flow construct.”
I disagree, but not entirely.
The problem is not the operator itself, but how it’s used. In many languages, developers naturally converge on a simple rule: use ternaries for small, single-condition expressions, and avoid nesting them. The unreadable cases the Go team warns about are real, but they are also avoidable with basic discipline.
Banning the construct entirely removes both the bad uses and the good ones.
And if the goal is to prevent unreadable code, then consistency becomes tricky. Go does not prevent deeply nested if statements, even though they can become just as hard to follow. Instead, it relies on developers to recognize when complexity is getting out of hand.
A simple example
The most common case, a label based on a condition:
// C++
label := isAdmin ? "admin" : "guest";// Go
label := "guest"
if isAdmin {
label = "admin"
}Where it really stings is with bitmasks
// C++
int flags = READ | (isAdmin ? WRITE : 0) | (isOwner ? EXEC : 0);This is compact, but still readable: each condition contributes directly to the final value.
In Go, the equivalent becomes:
flags := READ
if isAdmin {
flags |= WRITE
}
if isOwner {
flags |= EXEC
}This is perfectly idiomatic, but it spreads a single expression across multiple statements, making it harder to scan as a whole.
A more compact Go-style compromise might look like:
flags := READ
if isAdmin { flags |= WRITE }
if isOwner { flags |= EXEC }Still explicit, but less vertically noisy.
The difference here is not about correctness, but about density. The C++ version expresses the full intent in one place, while the Go version distributes it over time.
That said, I get it. Go’s north star is readability, and the ternary operator can get ugly fast, especially when nested. If you’ve ever stared at something like a ? b ? c : d : e, you know exactly what the Go team was trying to avoid.
So I accept it, move on, and write the if-else. It’s more verbose, but the codebase stays consistent and nobody complains in code review.
The ternary is not about complexity, it’s about density.
Go consistently chooses clarity over density and that trade-off is deliberate.
