Operators
Arithmetic, comparison, logical, and bitwise, plus what Go leaves out on purpose.
- Predict the output of integer division,
%, and shifts such as3 << 10. - Write a boolean expression with
%,&&and||that answers a yes/no question. - Explain why
d != 0 && 10/d > 2never divides by zero.
What an operator is
An operator is a symbol that does something to one or two values: + adds them, == compares them, && combines two true-or-false answers. They are the smallest building blocks of any expression you write.
Go's operators are worth a proper look for two reasons. A few behave differently from other languages: integer division throws away the remainder, and ++ is a statement rather than something you can use inside an expression. And a few that other languages have, Go leaves out on purpose.
Arithmetic
a, b := 10, 3fmt.Println(a + b) // 13fmt.Println(a - b) // 7fmt.Println(a * b) // 30fmt.Println(a / b) // 3 — INTEGER division truncates, it does not roundfmt.Println(a % b) // 1 — remainderfmt.Println(float64(a) / float64(b)) // 3.3333333333333335 — convert first if you want a decimala++ // a statement, NOT an expression. "x := a++" does not compile.a-- // and there is no ++a eitherAssignment
x := 10 // declare and assign (only inside a function)x = 20 // assign to an existing variablevar y int = 5 // declare with an explicit typefmt.Println(x, y)x += 5 // x = x + 5x -= 5x *= 2x /= 2x %= 3x <<= 1 // the bitwise ones work toox &= 0xFFa, b := 1, 2a, b = b, a // swap in one line, no temp variablefmt.Println(a, b) // 2 1Comparison
package mainimport "fmt"func main() { a, b := 3, 5 fmt.Println(a == b) // false fmt.Println(a != b) // true fmt.Println(a < b, a >= b) // true false fmt.Println(2.5 > 2.0) // true — floats compare the same way fmt.Println("apple" < "banana") // true — strings compare byte by byte fmt.Println("Go" == "go") // false — case matters fmt.Println(true == !false) // true — bools compare with == and != only // Both sides must be the same type. This does not compile: // fmt.Println(a == 2.5) // a is an int, 2.5 is not}Logical, and short-circuiting
package mainimport "fmt"func main() { a, b := true, false fmt.Println(a && b) // false — and fmt.Println(a || b) // true — or fmt.Println(!a) // false — not // Both short-circuit: if the left side already decides the answer, // the right side is never run. That is what makes this safe: d := 0 if d != 0 && 10/d > 2 { // 10/d is never reached when d is 0 fmt.Println("big") } else { fmt.Println("d is zero, skipped the division") }}Bitwise
These work on the individual bits (the 1s and 0s) of an integer. %b prints a number in binary so you can watch them.
Shifts move every bit left or right. x << n moves the bits n places left, which is the same as multiplying by 2, n times. x >> n moves them right, which is dividing by 2, n times (rounding down). So 1 << 10 is 1024 and 10 << 20 is 10 × 2²⁰ = 10 × 1,048,576, which is 10 MB. You will see that last one in real code as an upload size limit.
a, b := 0b1100, 0b1010fmt.Printf("%04b\n", a&b) // 1000 ANDfmt.Printf("%04b\n", a|b) // 1110 ORfmt.Printf("%04b\n", a^b) // 0110 XORfmt.Printf("%04b\n", a&^b) // 0100 AND NOT (bit clear) — this one is unique to Gofmt.Println(a<<2, a>>2) // 48 3 — shift left (a * 4), shift right (a / 4, rounding down)// A common use: flags packed into one integerconst ( CanRead = 1 << iota // 1 CanWrite // 2 CanAdmin // 4)perms := CanRead | CanWrite // set two flagshas := perms&CanWrite != 0 // test oneperms &^= CanWrite // clear onefmt.Println(has, perms) // true 1// Shifts as sizes: 10 << 20 is 10 * 1,048,576 bytesconst maxUpload = 10 << 20 // 10 MBfmt.Println(maxUpload) // 10485760Two operators for later
You will also meet & and * in front of a variable, and <- next to a channel. Those are not maths: they belong to pointers and channels, and each gets its own lesson.
p := &x // p holds the address of xv := <-ch // take a value out of channel chWhat Go leaves out on purpose
- No ternary `?:`. Write the
if. The Go FAQ says the readability cost of nested ternaries is not worth the saved line. - No operator overloading. You cannot define
+for your own struct.a + bis numeric addition, or concatenation for strings, and nothing else. - No pointer arithmetic. You cannot do
p + 1to walk memory (outside theunsafepackage). That is a large part of what makes Go memory-safe. - No `++` as an expression. It is a statement, so
arr[i++]does not compile, and neither do the order-of-evaluation bugs it allows in C.
* above + above comparison above && above ||. When in doubt, add brackets. gofmt keeps them, and the next reader does not have to remember the table.Try it yourself
package mainimport "fmt"const ( CanRead = 1 << iota // 1 CanWrite // 2 CanAdmin // 4)func loud(name string, v bool) bool { fmt.Println(" evaluated", name) return v}func main() { fmt.Println("7 / 2 =", 7/2) // 3, truncated fmt.Println("7.0 / 2 =", float64(7)/2) // 3.5 fmt.Println("short-circuit && :") _ = loud("left", false) && loud("right", true) // right never runs perms := CanRead | CanWrite fmt.Printf("perms = %04b\n", perms) fmt.Println("can write? ", perms&CanWrite != 0) perms &^= CanWrite fmt.Printf("after clear= %04b\n", perms)}- Integer division truncates. Convert to float64 first if you want a decimal.
- Comparisons give a
bool, and both sides must be the same type. &&and||short-circuit, which is what makesx != nil && x.Fieldsafe.- No ternary, no operator overloading, no pointer arithmetic. All three are left out on purpose.
a + b means addition, or concatenation for strings, in every file you will ever read.iota bit flags: set two permissions, test for one, clear one, and print the result with %b to watch the bits move.IsLeapYear(year int) bool using only %, ==, !=, && and || (no if). A year is a leap year if it divides by 4, except years that divide by 100, unless they also divide by 400. Examples: 2024 → true, 1900 → false, 2000 → true, 2023 → false.Tab indents. Press Esc, then Tab, to leave the editor.
- 1Print
17 / 5,17 % 5, andfloat64(17) / 5. Predict each before you run it. - 2Write a permissions integer with three
iotabit flags. Set two, test one, clear one, and print with%03bafter each step. - 3Prove short-circuiting to yourself: write
f()andg()that each print their name and return a bool, then runf() && g(). Makefreturn false and see thatgnever runs. - 4Without running it, work out
1 << 3,40 >> 2, and3 << 10. Then check.