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
| Operator | Meaning | Works on |
|---|---|---|
+ | Addition; also joins strings | Numbers, strings |
- * / | Subtraction, multiplication, division | Numbers |
% | Remainder | Integers only |
++ -- | Increment, decrement (statements, not expressions) | Numbers |
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) }
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:
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 }
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:
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)) }
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:
| Operator | Name | Result bit is 1 when… |
|---|---|---|
& | AND | both bits are 1 |
| | OR | either 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 / right | bits move by the given count |
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) }
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
| Operator | Meaning | Lesson |
|---|---|---|
&x | Address of x (makes a pointer) | 37 |
*p | The value a pointer points to | 37 |
<-ch, ch <- v | Receive from / send to a channel | 52 |
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:
| Level | Operators |
|---|---|
| 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:
package main import "fmt" func main() { fmt.Println(2+3*4, (2+3)*4) fmt.Println(1 + 2<<3) fmt.Println(true || false && false) }
14 20 17 true
What Go leaves out
- No ternary operator. There's no
cond ? a : b; write anifstatement (lesson 14). - No
**power operator. Usemath.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++]andy = 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.
1900 false 2000 true 2024 true 2026 false
fmt.