What is the idiomatic Go equivalent of C's ternary operator?

In C/C++ (and many languages of that family), a common idiom to declare and initialize a variable depending on a condition uses the ternary conditional operator :

int index = val > 0 ? val : -val 

Go doesn't have the conditional operator. What is the most idiomatic way to implement the same piece of code as above ? I came to the following solution, but it seems quite verbose

var index int if val > 0 { index = val } else { index = -val } 

Is there something better ?

8

12 Answers

Answer recommended by Go Language

As pointed out (and hopefully unsurprisingly), using if+else is indeed the idiomatic way to do conditionals in Go.

In addition to the full blown var+if+else block of code, though, this spelling is also used often:

index := val if val <= 0 { index = -val } 

and if you have a block of code that is repetitive enough, such as the equivalent of int value = a <= b ? a : b, you can create a function to hold it:

func min(a, b int) int { if a <= b { return a } return b } ... value := min(a, b) 

The compiler will inline such simple functions, so it's fast, more clear, and shorter.

15

No Go doesn't have a ternary operator, using if/else syntax is the idiomatic way.

Why does Go not have the ?: operator?

There is no ternary testing operation in Go. You may use the following to achieve the same result:

if expr { n = trueVal } else { n = falseVal } 

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.

— Frequently Asked Questions (FAQ) - The Go Programming Language

9

Suppose you have the following ternary expression (in C):

int a = test ? 1 : 2; 

The idiomatic approach in Go would be to simply use an if block:

var a int if test { a = 1 } else { a = 2 } 

However, that might not fit your requirements. In my case, I needed an inline expression for a code generation template.

I used an immediately evaluated anonymous function:

a := func() int { if test { return 1 } else { return 2 } }() 

This ensures that both branches are not evaluated as well.

5

The map ternary is easy to read without parentheses:

c := map[bool]int{true: 1, false: 0} [5 > 4] 
11

Foreword: Without arguing that if else is the way to go, we can still play with and find pleasure in language-enabled constructs.

Go 1.18 generics update: Go 1.18 adds generics support. It is now possible to create a generic If() function like this. Note: This is available in , as gog.If() (disclosure: I'm the author).

func If[T any](cond bool, vtrue, vfalse T) T { if cond { return vtrue } return vfalse } 

Which you can use like this:

min := If(i > 0, i, 0) 

The pre-1.18 answer follows:


The following If construct is available in my library with lots of other methods, being the gox.If type.


Go allows to attach methods to any user-defined types, including primitive types such as bool. We can create a custom type having bool as its underlying type, and then with a simple type conversion on the condition, we have access to its methods. Methods that receive and select from the operands.

Something like this:

type If bool func (c If) Int(a, b int) int { if c { return a } return b } 

How can we use it?

i := If(condition).Int(val1, val2) // Short variable declaration, i is of type int |-----------| \ type conversion \---method call 

For example a ternary doing max():

i := If(a > b).Int(a, b) 

A ternary doing abs():

i := If(a >= 0).Int(a, -a) 

This looks cool, it's simple, elegant, and efficient (it's also eligible for inlining).

One downside compared to a "real" ternary operator: it always evaluates all operands.

To achieve deferred and only-if-needed evaluation, the only option is to use functions (either declared functions or methods, or function literals), which are only called when / if needed:

func (c If) Fint(fa, fb func() int) int { if c { return fa() } return fb() } 

Using it: Let's assume we have these functions to calculate a and b:

func calca() int { return 3 } func calcb() int { return 4 } 

Then:

i := If(someCondition).Fint(calca, calcb) 

For example, the condition being current year > 2020:

i := If(time.Now().Year() > 2020).Fint(calca, calcb) 

If we want to use function literals:

i := If(time.Now().Year() > 2020).Fint( func() int { return 3 }, func() int { return 4 }, ) 

Final note: if you would have functions with different signatures, you could not use them here. In that case you may use a function literal with matching signature to make them still applicable.

For example if calca() and calcb() would have parameters too (besides the return value):

func calca2(x int) int { return 3 } func calcb2(x int) int { return 4 } 

This is how you could use them:

i := If(time.Now().Year() > 2020).Fint( func() int { return calca2(0) }, func() int { return calcb2(0) }, ) 

Try these examples on the Go Playground.

func Ternary(statement bool, a, b interface{}) interface{} { if statement { return a } return b } func Abs(n int) int { return Ternary(n >= 0, n, -n).(int) } 

This will not outperform if/else and requires cast but works. FYI:

BenchmarkAbsTernary-8 100000000 18.8 ns/op

BenchmarkAbsIfElse-8 2000000000 0.27 ns/op

2

If all your branches make side-effects or are computationally expensive the following would a semantically-preserving refactoring:

index := func() int { if val > 0 { return printPositiveAndReturn(val) } else { return slowlyReturn(-val) // or slowlyNegate(val) } }(); # exactly one branch will be evaluated 

with normally no overhead (inlined) and, most importantly, without cluttering your namespace with a helper functions that are only used once (which hampers readability and maintenance). Live Example

Note if you were to naively apply Gustavo's approach:

 index := printPositiveAndReturn(val); if val <= 0 { index = slowlyReturn(-val); // or slowlyNegate(val) } 

you'd get a program with a different behavior; in case val <= 0 program would print a non-positive value while it should not! (Analogously, if you reversed the branches, you would introduce overhead by calling a slow function unnecessarily.)

4

One-liners, though shunned by the creators, have their place.

This one solves the lazy evaluation problem by letting you, optionally, pass functions to be evaluated if necessary:

func FullTernary(e bool, a, b interface{}) interface{} { if e { if reflect.TypeOf(a).Kind() == reflect.Func { return a.(func() interface{})() } return a } if reflect.TypeOf(b).Kind() == reflect.Func { return b.(func() interface{})() } return b } func demo() { a := "hello" b := func() interface{} { return a + " world" } c := func() interface{} { return func() string { return "bye" } } fmt.Println(FullTernary(true, a, b).(string)) // cast shown, but not required fmt.Println(FullTernary(false, a, b)) fmt.Println(FullTernary(true, b, a)) fmt.Println(FullTernary(false, b, a)) fmt.Println(FullTernary(true, c, nil).(func() string)()) } 

Output

hello hello world hello world hello bye 
  • Functions passed in must return an interface{} to satisfy the internal cast operation.
  • Depending on the context, you might choose to cast the output to a specific type.
  • If you wanted to return a function from this, you would need to wrap it as shown with c.

The standalone solution here is also nice, but could be less clear for some uses.

2

As others have noted, golang does not have a ternary operator or any equivalent. This is a deliberate decision thought to improve readability.

This recently lead me to a scenario where constructing a bit-mask in a very efficient manner became hard to read when written idiomatically, or very inefficient when encapsulated as a function, or both, as the code produces branches:

package lib func maskIfTrue(mask uint64, predicate bool) uint64 { if predicate { return mask } return 0 } 

producing:

 text "".maskIfTrue(SB), NOSPLIT|ABIInternal, $0-24 funcdata $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) funcdata $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) movblzx "".predicate+16(SP), AX testb AL, AL jeq maskIfTrue_pc20 movq "".mask+8(SP), AX movq AX, "".~r2+24(SP) ret maskIfTrue_pc20: movq $0, "".~r2+24(SP) ret 

What I learned from this was to leverage a little more Go; using a named result in the function (result int) saves me a line declaring it in the function (and you can do the same with captures), but the compiler also recognizes this idiom (only assign a value IF) and replaces it - if possible - with a conditional instruction.

func zeroOrOne(predicate bool) (result int) { if predicate { result = 1 } return } 

producing a branch-free result:

 movblzx "".predicate+8(SP), AX movq AX, "".result+16(SP) ret 

which go then freely inlines.

package lib func zeroOrOne(predicate bool) (result int) { if predicate { result = 1 } return } type Vendor1 struct { Property1 int Property2 float32 Property3 bool } // Vendor2 bit positions. const ( Property1Bit = 2 Property2Bit = 3 Property3Bit = 5 ) func Convert1To2(v1 Vendor1) (result int) { result |= zeroOrOne(v1.Property1 == 1) << Property1Bit result |= zeroOrOne(v1.Property2 < 0.0) << Property2Bit result |= zeroOrOne(v1.Property3) << Property3Bit return } 

produces

 movq "".v1+8(SP), AX cmpq AX, $1 seteq AL xorps X0, X0 movss "".v1+16(SP), X1 ucomiss X1, X0 sethi CL movblzx AL, AX shlq $2, AX movblzx CL, CX shlq $3, CX orq CX, AX movblzx "".v1+20(SP), CX shlq $5, CX orq AX, CX movq CX, "".result+24(SP) ret 

eold's answer is interesting and creative, perhaps even clever.

However, it would be recommended to instead do:

var index int if val > 0 { index = printPositiveAndReturn(val) } else { index = slowlyReturn(-val) // or slowlyNegate(val) } 

Yes, they both compile down to essentially the same assembly, however this code is much more legible than calling an anonymous function just to return a value that could have been written to the variable in the first place.

Basically, simple and clear code is better than creative code.

Additionally, any code using a map literal is not a good idea, because maps are not lightweight at all in Go. Since Go 1.3, random iteration order for small maps is guaranteed, and to enforce this, it's gotten quite a bit less efficient memory-wise for small maps.

As a result, making and removing numerous small maps is both space-consuming and time-consuming. I had a piece of code that used a small map (two or three keys, are likely, but common use case was only one entry) But the code was dog slow. We're talking at least 3 orders of magnitude slower than the same code rewritten to use a dual slice key[index]=>data[index] map. And likely was more. As some operations that were previously taking a couple of minutes to run, started completing in milliseconds.\

3

I have compiled some items and compared the speed.

/* go test ternary_op_test.go -v -bench="^BenchmarkTernaryOperator" -run=none -benchmem */ package _test import ( "testing" ) func BenchmarkTernaryOperatorIfElse(b *testing.B) { for i := 0; i < b.N; i++ { if i%2 == 0 { _ = i } else { _ = -i } } } // func Ternary(statement bool, a, b interface{}) interface{} { if statement { return a } return b } func BenchmarkTernaryOperatorTernaryFunc(b *testing.B) { for i := 0; i < b.N; i++ { _ = Ternary(i%2 == 0, i, -i).(int) } } // func BenchmarkTernaryOperatorWithFunc(b *testing.B) { for i := 0; i < b.N; i++ { _ = func() int { if i%2 == 0 { return i } else { return -i } } } } // func BenchmarkTernaryOperatorMap(b *testing.B) { for i := 0; i < b.N; i++ { _ = map[bool]int{true: i, false: -i}[i%2 == 0] } } 

output

goos: windows goarch: amd64 cpu: Intel(R) Core(TM) i7-8565U CPU @ 1.80GHz BenchmarkTernaryOperatorIfElse BenchmarkTernaryOperatorIfElse-8 1000000000 0.4460 ns/op 0 B/op 0 allocs/op BenchmarkTernaryOperatorTernaryFunc BenchmarkTernaryOperatorTernaryFunc-8 1000000000 0.3602 ns/op 0 B/op 0 allocs/op BenchmarkTernaryOperatorWithFunc BenchmarkTernaryOperatorWithFunc-8 659517496 1.642 ns/op 0 B/op 0 allocs/op BenchmarkTernaryOperatorMap BenchmarkTernaryOperatorMap-8 13429532 82.48 ns/op 0 B/op 0 allocs/op PASS ok command-line-arguments 4.365s 

One more suggestion for the idiomatic approach in Go of ternary operator:

package main import ( "fmt" ) func main() { val := -5 index := func (test bool, n, d int) int { if test { return n } return d }(val > 0, val, -val) fmt.Println(index) } 

Go Playground

Your Answer

Sign up or log in

Sign up using Google Sign up using Facebook Sign up using Email and Password

Post as a guest

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

You Might Also Like