AI generates if/else blocks and operator expressions constantly. Most of the time they're correct, Go's operators are straightforward. But there are specific traps where AI-generated Go behaves differently than you'd expect from Python or JavaScript experience. This lesson focuses on those traps.
Arithmetic operators
The basics work as expected:
sum := 10 + 3 // 13
diff := 10 - 3 // 7
product := 10 * 3 // 30
quotient := 10 / 3 // 3 - integer division!
remainder := 10 % 3 // 1That 10 / 3 = 3 is the first trap. Integer division in Go truncates, it doesn't round, and it doesn't return a float. This matches C behavior but surprises people from Python 3 (where / returns a float).
| Expression | Go result | Python 3 result | Why different |
|---|---|---|---|
10 / 3 | 3 | 3.333... | Go: integer division truncates |
10 % 3 | 1 | 1 | Same behavior |
7 / 2 | 3 | 3.5 | Go: both operands are int → result is int |
7.0 / 2.0 | 3.5 | 3.5 | Both are float → float division |
-7 / 2 | -3 | -4 | Go truncates toward zero; Python floors |
To get float division with integer variables:
a, b := 10, 3
result := float64(a) / float64(b) // 3.333...percentage := count / total * 100 where count and total are both int. If count is 3 and total is 10, you get 0 * 100 = 0 instead of 30. The fix is to convert to float64 before dividing: percentage := float64(count) / float64(total) * 100.Comparison operators
x := 10
fmt.Println(x == 10) // true
fmt.Println(x != 5) // true
fmt.Println(x > 5) // true
fmt.Println(x <= 10) // trueGo only compares values of the same type. You can't compare an int to a float64 without converting:
var i int = 42
var f float64 = 42.0
// fmt.Println(i == f) // COMPILE ERROR
fmt.Println(float64(i) == f) // true - explicit conversionString comparison
Strings compare lexicographically (dictionary order) using ==, <, >:
fmt.Println("apple" < "banana") // true
fmt.Println("Go" == "Go") // true
fmt.Println("A" < "a") // true - uppercase letters come first in UTF-8A (byte 65) is "less than" lowercase a (byte 97). For case-insensitive comparison, use strings.EqualFold("Go", "go").Logical operators
x := 10
if x > 5 && x < 20 { // AND - both must be true
fmt.Println("in range")
}
if x < 5 || x > 8 { // OR - at least one must be true
fmt.Println("outside range")
}
if !false { // NOT - inverts the boolean
fmt.Println("true")
}Go uses short-circuit evaluation: if the left side of && is false, the right side never runs. If the left side of || is true, the right side never runs. This matters when the right side has side effects.
// Safe: isValid() only called if user is not nil
if user != nil && isValid(user) {
// ...
}if/else, no parentheses, braces required
Go's if statements look cleaner than C-family languages because parentheses around the condition are not needed. But braces are always required, even for single-line bodies.
if temperature > 30 {
fmt.Println("Hot")
} else if temperature > 20 {
fmt.Println("Warm")
} else {
fmt.Println("Cold")
}| Go rule | Other languages | Why Go is different |
|---|---|---|
| No parentheses around condition | if (x > 0) in C/Java/JS | Cleaner syntax, less noise |
| Braces always required | Optional for one-liners | Prevents bugs from adding lines later |
else must be on same line as } | Flexible in most languages | Go parser requirement |
Opening { on same line | Some allow next-line | Go parser requirement |
if blocks without braces for one-liners or puts else on a new line. Both are syntax errors:> // WRONG, no braces
> if x > 0
> fmt.Println("positive")
>
> // WRONG, else on new line
> if x > 0 {
> fmt.Println("positive")
> }
> else {
> fmt.Println("non-positive")
> }
>
> // CORRECT
> if x > 0 {
> fmt.Println("positive")
> } else {
> fmt.Println("non-positive")
> }
>The init statement: Go's secret weapon
Go has a feature most languages don't: you can declare a variable inside an if condition. The variable is scoped to the if/else block.
if err := doSomething(); err != nil {
fmt.Println("Error:", err)
return
}
// err doesn't exist here - it's out of scopeThe semicolon separates the init statement (err := doSomething()) from the condition (err != nil). This pattern is everywhere in Go because it keeps error-handling variables tightly scoped.
Compare with the non-init version:
err := doSomething()
if err != nil {
fmt.Println("Error:", err)
return
}
// err still exists here - "pollutes" the scopeBoth work, but the init statement version is more idiomatic. You'll see it in every Go codebase. AI generates both styles; the init statement version is preferred when you only need the variable inside the if block.
if v := compute(); v > threshold { ... }. But the convention is to use it primarily for error checks.Why there's no ternary operator
Coming from JavaScript (const x = cond ? a : b) or Python (x = a if cond else b), the lack of a ternary operator feels painful. It's deliberate.
Go's designers decided that ternary operators encourage writing dense, hard-to-read expressions. Instead, you write an if/else:
// What you can't write:
// status := age >= 18 ? "adult" : "minor"
// What you write instead:
var status string
if age >= 18 {
status = "adult"
} else {
status = "minor"
}result := condition ? valueA : valueB. This is a syntax error. There's no workaround; you must use if/else. If you see a ternary in AI-generated Go, rewrite it immediately.fmt.Printf format verbs
AI generates Printf calls frequently for formatted output. You need to read format verbs to verify the output matches what's intended:
name := "Alice"
age := 30
score := 95.5
fmt.Printf("Name: %s\n", name) // %s - string
fmt.Printf("Age: %d\n", age) // %d - decimal integer
fmt.Printf("Score: %.1f\n", score) // %.1f - float, 1 decimal
fmt.Printf("Score: %v\n", score) // %v - default format (any type)
fmt.Printf("Type: %T\n", score) // %T - prints the type itself
fmt.Printf("Quoted: %q\n", name) // %q - quoted string| Verb | Type | Example output |
|---|---|---|
%s | string | Alice |
%d | integer | 42 |
%f | float | 3.141590 |
%.2f | float (2 decimals) | 3.14 |
%v | any (default format) | Depends on type |
%T | any (type name) | int, string, etc. |
%t | bool | true |
%q | quoted string | "Alice" |
%x | hex | 2a |
%b | binary | 101010 |
%% | literal % | % |
%s for an integer or %d for a string. Go doesn't catch this at compile time (it's a runtime format string), but go vet does. Always run go vet on AI-generated code to catch Printf mismatches.Operator precedence
When AI generates complex expressions, knowing precedence helps you evaluate correctness without running the code:
| Precedence (high → low) | Operators |
|---|---|
| 1 (highest) | *, /, % |
| 2 | +, - |
| 3 | ==, !=, <, >, <=, >= |
| 4 | && |
| 5 (lowest) | \|\| |
// What does this evaluate to?
result := 2 + 3*4 > 10 && true
// Step 1: 3*4 = 12 (multiplication first)
// Step 2: 2 + 12 = 14 (addition)
// Step 3: 14 > 10 = true (comparison)
// Step 4: true && true (logical AND)
// result = trueWhen in doubt, use parentheses. Explicit grouping is always more readable than relying on precedence.
**). For exponentiation, use math.Pow(base, exp). AI trained on Python sometimes generates x ** 2 in Go, that's a syntax error.Combining it all: reading real Go patterns
Here's a typical AI-generated function. Read it and evaluate each line:
func categorize(scores []int) (int, int, int) {
var high, medium, low int // zero values: all 0
for _, score := range scores { // _ discards index
if pct := float64(score) / 100.0; pct >= 0.8 {
high++
} else if pct >= 0.5 {
medium++
} else {
low++
}
}
return high, medium, low
}This function uses: zero-value initialization, the blank identifier, float conversion to avoid integer division, an init statement in if, short-circuit else if chains, and multiple return values. Every pattern from this lesson and the previous ones, in nine lines.