Operators

Operators combine values into expressions. Go's set is small and mostly familiar: arithmetic, comparison, logical and bitwise operators, plus a few for pointers and channels that later lessons cover. This lesson goes through each group, explains short-circuit evaluation and Go's five precedence levels, and notes what Go deliberately leaves out.

Arithmetic operators

OperatorMeaningWorks on
+Addition; also joins stringsNumbers, strings
- * /Subtraction, multiplication, divisionNumbers
%RemainderIntegers only
++ --Increment, decrement (statements, not expressions)Numbers
GO
package main

import "fmt"

func main() {
	fmt.Println(7+2, 7-2, 7*2, 7/2, 7%2)
	fmt.Println(7.0 / 2)
	fmt.Println("Go" + "pher")

	n := 5
	n++
	fmt.Println(n, -n)
}
Output
9 5 14 3 1
3.5
Gopher
6 -6

Comparison operators

==, !=, <, <=, > and >= produce a bool. Both sides must have the same type. Strings compare byte by byte, which for plain English text means alphabetical order with all capitals before all lowercase letters:

GO
package main

import "fmt"

func main() {
	fmt.Println(3 < 5, 3 == 3.0, "apple" < "banana")
	fmt.Println("Zebra" < "apple", "go" == "Go")

	type Point struct{ X, Y int }
	fmt.Println(Point{1, 2} == Point{1, 2}) // structs compare field by field
}
Output
true true true
true false
true

Arrays and structs can be compared with == when all their elements or fields can. Slices, maps and functions can't (except with nil); lesson 27 shows slices.Equal for comparing slices.

Logical operators

&& (and), || (or) and ! (not) work only on booleans. && and || short-circuit: the right side is evaluated only if it can still change the result. That makes guards like this safe:

GO
package main

import "fmt"

func check(label string, result bool) bool {
	fmt.Println("  evaluating", label)
	return result
}

func main() {
	names := []string{}
	if len(names) > 0 && names[0] == "admin" { // names[0] is never touched
		fmt.Println("first is admin")
	}

	fmt.Println("&&:", check("left", false) && check("right", true))
	fmt.Println("||:", check("left", true) || check("right", false))
}
Output
  evaluating left
&&: false
  evaluating left
||: true

In both cases the right-hand check never ran, because the left side alone decided the answer.

Bitwise operators

These work on the individual bits of integers. They're used for flags, masks, hashing and binary formats:

OperatorNameResult bit is 1 when…
&ANDboth bits are 1
|OReither bit is 1
^XOR (binary); NOT (unary)the bits differ / flips every bit
&^AND NOT ("bit clear")the left bit is 1 and the right bit is 0
<< >>Shift left / rightbits move by the given count
GO
package main

import "fmt"

func main() {
	a, b := 0b1100, 0b1010
	fmt.Printf("%04b %04b %04b %04b\n", a&b, a|b, a^b, a&^b)
	fmt.Println(1<<4, 96>>2)

	var x uint8 = 0b0000_1111
	fmt.Printf("%08b\n", ^x)

	const (
		Bold      = 1 << 0
		Italic    = 1 << 1
		Underline = 1 << 2
	)
	style := Bold | Underline
	fmt.Println(style&Italic != 0, style&Underline != 0)
	style &^= Bold // turn the Bold flag off
	fmt.Println(style)
}
Output
1000 1110 0110 0100
16 24
11110000
false true
4

Shifting left by n multiplies by 2n; shifting right divides by it (rounding down).

Assignment operators

Every binary arithmetic and bitwise operator has a compound form: +=, -=, *=, /=, %=, &=, |=, ^=, &^=, <<= and >>=. x op= y means x = x op y.

Other operators

OperatorMeaningLesson
&xAddress of x (makes a pointer)37
*pThe value a pointer points to37
<-ch, ch <- vReceive from / send to a channel52

Precedence

Go has only five levels of precedence for binary operators. Higher levels bind tighter; operators on the same level are evaluated left to right:

LevelOperators
5 (highest)* / % << >> & &^
4+ - | ^
3== != < <= > >=
2&&
1 (lowest)||

Unary operators (-x, !ok, ^bits, &v, *p) bind tightest of all. Notice that shifts and & sit with multiplication, so 1 + 2<<3 is 1 + 16. When in doubt, add parentheses — gofmt even adjusts spacing to show how an expression groups:

GO
package main

import "fmt"

func main() {
	fmt.Println(2+3*4, (2+3)*4)
	fmt.Println(1 + 2<<3)
	fmt.Println(true || false && false)
}
Output
14 20
17
true

What Go leaves out

  • No ternary operator. There's no cond ? a : b; write an if statement (lesson 14).
  • No ** power operator. Use math.Pow, or a shift for powers of two.
  • No operator overloading. + always means what the language says, never a method call.
  • ++ and -- are statements. a[i++] and y = x++ don't compile.

Try it yourself

A year is a leap year if it's divisible by 4, except century years, which must be divisible by 400. Check a few years with one boolean expression.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
1900 false
2000 true
2024 true
2026 false
Next: formatting output with fmt.