What is the idiomatic Go equivalent of C’s ternary operator (condition ? a : b)? Use an explicit if / else, sometimes a switch, or a small function—there is no ?: in the language by design (Go FAQ). For value picks that match built-ins, prefer min and max (Go 1.21+). For “first non-zero default”, use cmp.Or (Go 1.22+). This page walks through those patterns, lazy evaluation, and when a tiny third-party helper is still optional.
For control-flow basics, see if / else, variable scope around short if declarations, loops, structs, and modules / getting started.
Tested with Go 1.24 on Linux (
min/maxrequire 1.21+;cmp.Orrequires 1.22+).
Why Go has no ?:
The FAQ states that if / else is intentionally the only form for that kind of conditional: ternary expressions were often used to build unreadable nested logic in other languages. Go trades one-line brevity for scan-friendly control flow.
Pattern 1: if / else assignment (the default)
Declare a variable, then assign in each branch—this is the direct analogue of result = cond ? a : b:
package main
import "fmt"
func main() {
age := 20
var label string
if age >= 18 {
label = "adult"
} else {
label = "minor"
}
fmt.Println(label)
}Output: adult.
Pattern 2: Short if with initialization
When the condition uses a value you only need in the if, use the init statement form (if details):
package main
import (
"fmt"
"strings"
)
func main() {
s := "hello"
if n := len(s); n > 4 {
fmt.Println("long:", n)
} else {
fmt.Println("short")
}
fmt.Println(strings.ToUpper(s))
}Pattern 3: switch instead of if / else if chains
When one expression drives several outcomes, a tagless switch on true or a switch on the value is idiomatic in Go and often clearer than stacked ternaries in C:
package main
import "fmt"
func grade(score int) string {
switch {
case score >= 90:
return "A"
case score >= 80:
return "B"
default:
return "C"
}
}
func main() {
fmt.Println(grade(85))
}Output: B.
Pattern 4: min / max (replaces many numeric ternaries)
A very common use of ?: in C is a < b ? a : b. Since Go 1.21, use the predeclared min and max; they accept multiple ordered arguments (including strings and floats):
package main
import "fmt"
func main() {
fmt.Println(min(3, 7))
fmt.Println(max("beta", "alpha"))
}Output:
3
betaFor the smallest or largest element of a slice, use slices.Min / slices.Max (Go 1.21+) with a non-empty slice.
Pattern 5: cmp.Or for “first non-zero, else default”
Not a boolean ternary, but the same place people reach for ?: when the condition is really “use x unless it is zero.” Since Go 1.22, cmp.Or returns the first argument not equal to the zero value of type T (T must be comparable):
package main
import (
"cmp"
"fmt"
)
func main() {
user := ""
fmt.Println(cmp.Or(user, "guest"))
fmt.Println(cmp.Or(0, 0, 42))
}Output:
guest
42If you need a predicate more complex than “non-zero,” stay with if / else.
Pattern 6: Lazy evaluation (only one branch runs)
Function arguments are always evaluated before the call. There is no language-level lazy ?:. To run only one side, use if / else or an immediately invoked function literal:
package main
import "fmt"
func main() {
val := -5
idx := func() int {
if val > 0 {
return val
}
return -val
}()
fmt.Println(idx)
}Output: 5. Use this shape when a branch calls something expensive or has side effects.
Pattern 7: Generic one-line pick (your own helper)
If you want a reusable “choose a or b” without a third-party module, a two-line generic function keeps lazy semantics only if you pass values already computed—for lazy work, pass func() thunks or use if / else:
package main
import "fmt"
func pick[T any](cond bool, a, b T) T {
if cond {
return a
}
return b
}
func main() {
fmt.Println(pick(5 > 3, "high", "low"))
}Output: high. The compiler can inline tiny helpers like this; readability still beats micro-shaving lines.
Pattern 8: Optional pointers and zero values
For *T, idiomatic code uses an explicit nil check rather than a ternary:
package main
import "fmt"
func label(p *string) string {
if p != nil {
return *p
}
return "anonymous"
}
func main() {
fmt.Println(label(nil))
s := "Ada"
fmt.Println(label(&s))
}Optional third-party module: github.com/julien040/go-ternary
The package on pkg.go.dev/github.com/julien040/go-ternary exposes ternary.If, ternary.IfFunc, and related helpers. Typical signature:
func If[T any](condition bool, a, b T) TAdd it with go get github.com/julien040/go-ternary@latest inside a module. Snippets below use {run=false} because they expect that dependency in go.mod.
package main
import (
"fmt"
"github.com/julien040/go-ternary"
)
func main() {
fmt.Println(ternary.If(true, "yes", "no"))
fmt.Println(ternary.If(len("abc") > 2, 10, 0))
}You should see yes then 10. Prefer upstream docs for IfFunc / Iff when you need deferred evaluation via function values.
Eager evaluation pitfall
Both a and b are evaluated before If runs. This program prints both side-effect lines:
package main
import (
"fmt"
"github.com/julien040/go-ternary"
)
func expensive() int { fmt.Println("expensive side"); return 1 }
func cheap() int { fmt.Println("cheap side"); return 2 }
func main() {
useFast := true
_ = ternary.If(useFast, expensive(), cheap())
}For lazy branching, use if / else or thunk-based APIs from the same module.
What not to do (readability)
- Deeply nested
ternary.If(or nestedpickcalls) usually loses to a flatswitchor local variables—same problem Go avoided by omitting?:. map[bool]Ttricks (m[cond]) save characters but obscure control flow; reviewers and tools treat explicitifmore kindly.
Summary
Go’s answer to “C ternary” is not a single operator: use if / else, switch, min / max, cmp.Or where it fits, function literals when a branch must not run, or a small helper (generic or not). Third-party ternary.If mimics syntax but eagerly evaluates both branch values—know that before using it in hot paths or when branches have side effects. Keep functions and methods straightforward unless a one-liner genuinely helps the reader.
References
- Go FAQ — Why does Go not have the
?:operator? - Package builtin —
min,max - Package cmp —
Or - Package slices —
Min,Max - julien040/go-ternary (module documentation)

