Go Module 2 – Basics: values, types and control flow
Operators, precedence and bit manipulation
Go's arithmetic, comparison, logical and bitwise operators, its five precedence levels, why it has no ?: or x++ inside expressions, and flags and masks.
What you will learn
- Use arithmetic, comparison, logical and bitwise operators correctly
- Apply Go's five precedence levels without extra parentheses
- Explain why Go has no ?: operator and why ++ is a statement
- Use &^, shifts and masks for flags and compact encodings
Before you start
What you’ll learn · Before you start
What you will learn
- Use arithmetic, comparison, logical and bitwise operators correctly
- Apply Go's five precedence levels without extra parentheses
- Explain why Go has no ?: operator and why ++ is a statement
- Use &^, shifts and masks for flags and compact encodings
Before you start
On this page
1 + 2<<1 is 5 in Go, not 6: a shift binds as tightly as a multiplication, so it runs before the addition. Go has
only five operator precedence: The rule that decides which operator of an expression is grouped first. Go has five levels for binary operators, from * / % << >> & &^ down to ||, and operators on one level group from left to right. levels for its binary operators, few enough to know by
heart, and with them most expressions need no parentheses.
You will use Go’s operators, group an expression by precedence, see why Go has no ?: and no x++ inside an
expression, and read the bitwise operators on real bits.
Operators need one type, and the logical ones stop early
Arithmetic takes two operands of the same type and gives a result of that type, so 499 / 3 between ints is 166 and
% gives the remainder, 1. + also joins strings. Comparisons give a bool, and strings compare byte by byte, which
puts “Zoo” before “apple”: ‘Z’ is the byte 90 and ‘a’ the byte 97. The logical operators && and || use
short-circuit evaluation: The way && and || work: the right operand is evaluated only when the left one does not decide the result, so a check on the left can protect the expression on the right.: they evaluate their right side only when the left
side does not settle the answer:
// operators_basics.go: arithmetic, comparison and logical operators.
package main
import "fmt"
func main() {
price, qty := 499, 3
fmt.Println(price*qty, price/qty, price%qty, -price)
fmt.Println("total" + " " + "due") // + also joins strings
fmt.Println(price > 400, price == 499, "apple" < "banana", "Zoo" < "apple")
codes := []string{"B2", "S4"}
i := 5
ok := i < len(codes) && codes[i] == "B2" // && stops before codes[5] could run
fmt.Println("in range and B2:", ok)
} Output
1497 166 1 -499 total due true true true true in range and B2: false
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run operators_basics.go
i < len(codes) is false, so codes[i] never runs, and the program does not panic on index 5. Put the cheap or
protecting test on the left of &&: a length check before an index, a nil check before a field.
Checkpoint
Check: Operators of one type, and short-circuit logic
Five precedence levels decide where the parentheses go
Every binary operator belongs to one of five levels, and the higher level is grouped first:
| Level | Operators | Grouped |
|---|---|---|
| 5 | * / % << >> & &^ |
first |
| 4 | + - | ^ |
second |
| 3 | == != < <= > >= |
third |
| 2 | && |
fourth |
| 1 | || |
last |
Unary operators, such as -x, !ok and ^x, bind more tightly than any of them, and operators of one level group from
left to right, so 10-4-3 is 3. The shifts and & sit with multiplication, and | with addition:
// precedence.go: the same operators, grouped by Go's five levels.
package main
import "fmt"
func main() {
a, b := 1, 2
fmt.Println(a + b<<1) // 5: << binds like *, so it runs before +
fmt.Println((a + b) << 1) // 6
flags, mask := 6, 4
fmt.Println(flags&mask == 0) // false: & runs before ==
fmt.Println(10-4-3, 100/10*2) // one level: left to right
n, ok := 7, true
fmt.Println(n+2<<1 > 9 && ok) // ((n + (2<<1)) > 9) && ok
} Output
5 6 false 3 20 true
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run precedence.go
Step through the last line, which needs no parentheses at all:
The drawing shows the whole scene; its 4 steps are listed below.
Every step as text
- The strongest level. Go first groups the operators of level 5. The shift 2<<1 belongs there, beside * and &, so it is worked out first and gives 4.
- Then addition. Level 4 holds + and -, and | and ^ as well. n + 4 is 7 + 4, which is 11. A reader who added first would have shifted 9 instead and got 18.
- Then the comparison. Comparisons are level 3, so 11 > 9 compares the finished sum and gives true. No comparison ever needs parentheses around the arithmetic on either side.
- Logic comes last. is level 2 and || level 1, so they join finished comparisons. true && ok is true, which the program printed.
flags&mask == 0 is read as (flags & mask) == 0, because & is on level 5 and == on level 3, so the bit test
needs no parentheses. Where a reader could still hesitate, add them anyway: they cost nothing.
Checkpoint
Check: The five precedence levels
Go has no conditional operator, and increments are statements
Two familiar operators are missing on purpose. There is no conditional operator ?::
// ternary.go: Go has no ?: operator.
package main
import "fmt"
func main() {
paid := true
label := paid ? "paid" : "due"
fmt.Println(label)
} Output (exit status 1)
Printed as an error (standard error)
# command-line-arguments ./ternary.go:8:16: invalid character U+003F '?' ./ternary.go:8:18: syntax error: unexpected literal "paid" at end of statement
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run ternary.go
And ++ and -- are statements, not expressions, so they cannot give a value:
// plusplus.go: ++ is a statement, so it cannot give a value.
package main
import "fmt"
func main() {
count := 1
next := count++
fmt.Println(next)
} Output (exit status 1)
Printed as an error (standard error)
# command-line-arguments ./plusplus.go:8:15: syntax error: unexpected ++ at end of statement
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run plusplus.go
Go’s FAQ gives the reasons. The designers had seen ?: used to build expressions that were hard to read, and an
if-else is clearer; making ++ a statement removes the questions about evaluation order that f(i++) raises.
Write label := "due" and then if paid { label = "paid" }, and write count++ on a line of its own.
Assignment is a statement as well, so it cannot stand where a value is needed. That makes the classic slip of = for
== in a condition a compile error instead of a bug:
// assign_if.go: an assignment where a condition belongs.
package main
import "fmt"
func main() {
x := 3
if x = 5 {
fmt.Println(x)
}
} Output (exit status 1)
Printed as an error (standard error)
# command-line-arguments ./assign_if.go:8:7: syntax error: cannot use assignment x = 5 as value
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run assign_if.go
The compiler stopped with cannot use assignment x = 5 as value, before the program could run.
Checkpoint
Check: No conditional operator, and ++ as a statement
Bitwise operators work on the bits of integers
Bit manipulation means working on the single bits of an integer, which is how flags, masks and compact encodings are built. Go has six bitwise operators for it, shown here on 12 and 10, which are 1100 and 1010 in binary:
// bitwise.go: the bit operators on 12 (1100) and 10 (1010).
package main
import "fmt"
func main() {
x, y := 12, 10
fmt.Printf("x %04b = %d\n", x, x)
fmt.Printf("y %04b = %d\n", y, y)
fmt.Printf("x & y %04b = %d\n", x&y, x&y)
fmt.Printf("x | y %04b = %d\n", x|y, x|y)
fmt.Printf("x ^ y %04b = %d\n", x^y, x^y)
fmt.Printf("x &^ y %04b = %d\n", x&^y, x&^y)
fmt.Printf("x << 1 %b = %d\n", x<<1, x<<1)
fmt.Printf("x >> 2 %04b = %d\n", x>>2, x>>2)
var u uint8 = 12
fmt.Printf("^u %08b = %d, for a uint8\n", ^u, ^u)
fmt.Println("^x =", ^x, "for an int, which is -x - 1")
} Output
x 1100 = 12 y 1010 = 10 x & y 1000 = 8 x | y 1110 = 14 x ^ y 0110 = 6 x &^ y 0100 = 4 x << 1 11000 = 24 x >> 2 0011 = 3 ^u 11110011 = 243, for a uint8 ^x = -13 for an int, which is -x - 1
Recorded with Go 1.27.1 on macOS 26 arm64. To run it yourself: go run bitwise.go
| Operator | Name | Each bit of the result | 12 and 10 |
|---|---|---|---|
& |
AND | set where both are set | 8, 1000 |
| |
OR | set where either is set | 14, 1110 |
^ |
XOR | set where exactly one is set | 6, 0110 |
&^ |
AND NOT | set in the left, cleared where the right is set | 4, 0100 |
<< >> |
shifts | moved left or right by the count | 24 and 3 |
^ has a second job: in front of one value it flips every bit, so ^12 is 243 for a uint8 and -13 for an int.
Bit manipulation needs integer operands, and it reads best with %b and %08b in a print. &^ clears bits,
mask: A value whose set bits pick out which bits of another value an operation keeps, clears or tests. keep the bits you name, shifts move them, and math/bits counts them.
Checkpoint
Check: The bitwise operators
Flags, masks and shifts: set, clear, toggle and test, reproduced Pro: opens with a Pro, Premium or Ultimate pass
Four expressions cover nearly everything done with flags: set with |, clear with &^, toggle with ^ and test with
&. Here they run on real file permissions, and then three colour channels are packed into one number and taken
out again.
In this part:
- Setting, clearing, toggling and testing permission bits
- Why AND NOT is the operator for clearing bits
- Packing three channels into a uint32
- Shifts at the edges: rounding down, and a negative count
1 diagram · 7 runnable examples (Go, Python) · about 6 minutes
Opening this part with your pass…
This part opens with any MySmartCoPilot pass.
- Pro pass · 1 week₹79$2.99
- 30 days₹179$5.99
- 1 year₹1,699$45
One payment; a pass never renews. The price shown is the total you pay: no fees or charges are added at checkout.
Passes can’t be bought right now, so this part can’t be opened at the moment. The free part above stands on its own. If you have a pass, enter your licence key.
It also opens every other in-depth part in Learn and every Pro tool. Sales are final, except where the law gives you a right to withdraw, for example in the EU and the UK (refund policy).
Comparisons and math/bits, measured Pro: opens with a Pro, Premium or Ultimate pass
== works on more than numbers: structs compare field by field and arrays element by element, while slices, maps and
functions compare only with nil. math/bits answers the questions that loops over bits used to answer: how many bits
are set, where the first one is, and whether a number is a power of two.
In this part:
- What equality compares, field by field
- Counting and finding bits with math/bits
- Flags, a struct of bools or a map: choosing with sizes
4 runnable examples (Go) · about 5 minutes
Opening this part with your pass…Pro part. It opens with the same pass as the part above.
Practise
Practise on operators, precedence, the missing ?: and the bitwise operators. Your shakiest idea is asked about
first, and the feedback on each choice says why it is right or where it goes wrong:
Practice that adapts to you
Practise this lesson
11 questions about 6 ideas of this lesson. Each answer is explained at once, and what you get right is kept in this browser.
Interview questions
Warm-up (fresher): How do you write a conditional value in Go, which has no ?: operator?
With an if. Give the variable its default first, then change it when the condition holds:
label := "due", then if paid { label = "paid" }. When both branches compute something, declare the variable with
var and assign it in an if-else. For a value used in many places, a small function returns it.
Three longer questions of the module bank build on this lesson:
-
Mid to senior
You port a checksum routine from C to Go line for line, and its results change. One line is crc = crc << 4 + x & 0xF. Which operators group differently in the two languages, and how do you port bit-level code safely?
-
Fresher to mid
A permissions field is a uint8 of flags. Write the expressions that set, clear, toggle and test one flag, and explain why flags & ^1 does not compile while flags &^ 1 does.
-
Fresher to mid
A function packs colours with uint32(r<<16 | g<<8 | b), where r, g and b are uint8 values, and every packed colour comes out as just its blue part. Why, and how do you write and test the packing so the bug cannot return?
Key takeaways
- Arithmetic needs operands of one type, comparisons give a bool, and
&&and||skip their right side when the left decides. - Five precedence levels group binary operators:
*and the shifts and&first, then+and|, then comparisons, then&&, then||. - Go has no
?:, so a conditional value is anif, and++is a statement on a line of its own. - The bitwise operators
&,|,^,&^,<<and>>work on the bits of integers, and^xflips every bit.
Terms in this lesson
- mask
- A value whose set bits pick out which bits of another value an operation keeps, clears or tests.
- operator precedence
- The rule that decides which operator of an expression is grouped first. Go has five levels for binary operators, from * / % << >> & &^ down to ||, and operators on one level group from left to right.
- short-circuit evaluation
- The way && and || work: the right operand is evaluated only when the left one does not decide the result, so a check on the left can protect the expression on the right.
References
- The Go Programming Language Specification: Operators (The Go Authors)
- The Go Programming Language Specification: Operator precedence (The Go Authors)
- The Go Programming Language Specification: Arithmetic operators (The Go Authors)
- The Go Programming Language Specification: Comparison operators (The Go Authors)
- The Go Programming Language Specification: Logical operators (The Go Authors)
- The Go Programming Language Specification: IncDec statements (The Go Authors)
- Frequently Asked Questions: Why does Go not have the ?: operator? (The Go Authors)
- Frequently Asked Questions: Why are ++ and -- statements and not expressions? (The Go Authors)
- Package math/bits (The Go Authors)
- Package io/fs: FileMode (The Go Authors)
- Package slices: Equal (The Go Authors)
- Command vet: shift (The Go Authors)
- Go 1.13 Release Notes: Changes to the language (The Go Authors)
Related tools
Report a problem with this lesson
Kept only in this browser. Your Learn progress