Last Updated: May 22, 2026
Operators are the symbols that combine values into expressions: adding prices, comparing stock counts, toggling permission flags, building cart totals. Go's operator set is small and predictable, but a few details to be aware of: integer division truncates decimals and i++ cannot be used inside another expression. This lesson covers every operator you'll use day to day.
Go has the five arithmetic operators you'd expect: +, -, *, /, and % (remainder). They work on numeric types like int, float64, and friends.
Two things to call out here. First, Go does not auto-convert between numeric types, so multiplying a float64 by an int requires an explicit float64(quantity). Second, % (the remainder operator) works only on integers. Try it on a float64 and the compiler complains.
When both operands are integers, / does integer division and throws away the fractional part. This is easy to overlook.
5 / 2 gives 2, not 2.5. There's no rounding, no error, just truncation toward zero. If you wanted a decimal result, you'd need at least one operand to be a float:
The % operator is the partner to integer division. It returns whatever's left after dividing, which is why it's perfect for "how many leftover items don't fit evenly?" or "is this number even?" checks.
Cost: Integer division is one CPU instruction. Float division is several. For tight loops over millions of items, integers win, but for everyday code the difference doesn't matter.
Comparison operators return a bool. Go has the six you'd expect: ==, !=, <, <=, >, >=.
Two rules govern comparisons in Go:
int to a float64 without converting one of them. The compiler rejects 5 == 5.0 with mismatched types int and float64.nil). Structs are comparable if all their fields are.For strings, == checks character-by-character equality, and <, >, and friends compare lexicographically (byte by byte, treating each byte as its numeric value).
"books" is less than "boots" because at the third character, 'o' is greater than 'o'... wait, those are equal. Look at the fourth: 'k' (107) versus 't' (116). 'k' is smaller, so "books" sorts first. This is how sort.Strings orders things by default.
Watch out: Lexicographic order compares raw bytes. For uppercase versus lowercase, 'A' (65) is less than 'a' (97), so "Apple" < "apple" is true. If you want case-insensitive comparison, lowercase both sides first with strings.ToLower.
Three logical operators: && (AND), || (OR), ! (NOT). They work only on bool values.
Go short-circuits && and ||. With a && b, if a is false, Go never evaluates b. With a || b, if a is true, Go never evaluates b. This isn't just a speedup, it's a tool you actively use to guard against nil checks and division by zero.
If Go evaluated both sides, the first if would divide by zero. Short-circuit evaluation is what makes patterns like if user != nil && user.IsActive() safe.
Bitwise operators treat integers as sequences of bits and operate on each bit position. They're less common in everyday business logic, but you'll see them in permission flags, feature toggles, packed configuration values, and compact storage.
| Operator | Name | What It Does |
|---|---|---|
& | AND | Bit is 1 only if both operands have 1 at that position |
| | OR | Bit is 1 if either operand has 1 at that position |
^ | XOR | Bit is 1 if exactly one operand has 1 at that position |
&^ | AND NOT (bit clear) | Bit is 1 if left has 1 and right has 0 |
<< | Left shift | Move bits left, fill right with 0 (multiply by 2 per shift) |
>> | Right shift | Move bits right, drop low bits (divide by 2 per shift) |
The &^ operator is unusual: most languages don't have it. a &^ b clears every bit in a that's set in b. Think of it as "AND with the complement of b".
A classic use case is packing multiple yes/no flags into one integer. Each bit position represents a permission.
The pattern is:
flags | X adds permission X.flags &^ X removes permission X.flags & X != 0 checks if X is set.flags ^ X toggles X.<< and >> shift bits left or right. Each left shift doubles the value, each right shift halves it (for non-negative integers).
Cost: Shifts are single-instruction operations on the CPU and beat multiplication or division for powers of two. In practice the compiler often optimizes n * 2 into a shift anyway, so don't use shifts purely for speed in regular code. Use them when the bit-level intent matters.
The basic assignment is =. Go also has compound assignment operators that combine an operation with assignment.
| Operator | Equivalent To | Example Use |
|---|---|---|
+= | x = x + y | Adding an item's price to a running cart total |
-= | x = x - y | Decreasing stock when an order ships |
*= | x = x * y | Applying a multiplier to a price |
/= | x = x / y | Scaling a value down |
%= | x = x % y | Reducing to a remainder |
&= | x = x & y | Masking off bits |
|= | x = x | y | Setting bits |
^= | x = x ^ y | Toggling bits |
<<= | x = x << y | Shifting left |
>>= | x = x >> y | Shifting right |
The most common one in beginner code is +=, especially in totals.
Compound operators are not just shorter, they often read more naturally. total += price says "add this price to the total" in a way that total = total + price does not.
One thing to remember: +=, -=, and the rest are statements, not expressions. You can't write result := (x += 5) like in some other languages.
Go has i++ and i--, but they behave differently than in most languages.
So far, so normal. Here's where Go differs:
i++ (postfix), not ++i. Writing ++i is a syntax error.Both rules exist to keep code unambiguous. In languages with prefix and postfix forms, expressions like a[i++] = b[++j] are easy to write and hard to read. Go sidesteps the problem entirely by making increments standalone statements.
If you wanted to use the new value in an expression, you increment first, then use it:
When an expression has multiple operators, Go evaluates them in a fixed order. Go has only 5 precedence levels for binary operators, which is fewer than most languages and easier to remember.
| Level | Operators | Type |
|---|---|---|
| 5 (highest) | * / % << >> & &^ | Multiplicative |
| 4 | + - | ^ | Additive |
| 3 | == != < <= > >= | Comparison |
| 2 | && | Logical AND |
| 1 (lowest) | || | Logical OR |
Unary operators (!, -x, *p, &x, ^x) bind tighter than any binary operator. So !a && b means (!a) && b, not !(a && b).
A few worked examples:
The third example has a non-obvious evaluation order. Reading left to right makes you think (true || false) && false is false. But && binds tighter than ||, so it's actually true || (false && false), which is true || false, which is true.
When in doubt, add parentheses. They're free and they make intent explicit:
Style note: Go developers rarely lean on memorized precedence for anything beyond * versus +. If a reader has to think about precedence for more than a second, the code probably needs parentheses.
+Go uses + to concatenate strings. The two operands have to be strings; you can't + a string and an integer.
For a handful of concatenations, + is fine. But there's a trap when you do it in a loop.
Cost: Strings in Go are immutable. Every s = s + part allocates a new string and copies all the existing bytes. Doing this n times in a loop is O(n^2) work and burns memory. For loop-based string building, use strings.Builder.
So this is fine:
But this is not:
Go has a few more operator-like symbols you'll encounter:
&x takes the address of x (produces a pointer to it).*p dereferences pointer p (gives the value it points to).ch <- value sends value to channel ch.value := <-ch receives a value from channel ch.& and * are unary in this context (operating on one value), not the bitwise & or arithmetic * you saw above. Go reuses the symbols, and the compiler tells them apart by where they appear.
Here's an example that uses most of what this lesson covered: arithmetic for a cart total, compound assignment, comparison for filters, logical operators for eligibility, and bitwise for user permissions.
Every operator here is doing something the others can't easily replace. The arithmetic builds the total, += keeps the loop readable, comparison drives the coupon check, bitwise tracks permissions compactly, and integer division plus % handles pagination.