Chapter 29: Logical
Logical operators — the operators you use to combine conditions, make complex decisions, guard against nil pointers, check multiple requirements at once, etc.
Logical operators are extremely common in real Go code — almost every non-trivial if, for, switch, validation logic, filter, permission check, etc. uses them.
Go keeps logical operators very simple:
- Only three logical operators exist
- They work only on bool values
- They have short-circuit evaluation (very important safety feature)
- No compound assignment versions (&&=, ||=) — intentional design choice
Let’s go through them slowly and carefully, like we’re sitting together with VS Code open, writing small examples and printing results.
1. The Three Logical Operators in Go
| Operator | Name | Meaning | Example expression | Result when… | Short-circuits? |
|---|---|---|---|---|---|
| && | Logical AND | true only if both operands are true | a && b | a = true AND b = true | Yes |
|
Logical OR | true if at least one is true | `a | ||
| ! | Logical NOT | reverses the value | !a | !true = false, !false = true | No |
2. Truth Table (Quick Reference)
| A | B | A && B | A || B | !A | |——-|——-|——–|——–|——-| | true | true | true | true | false | | true | false | false | true | false | | false | true | false | true | true | | false | false | false | false | true |
3. Runnable Examples — Copy-Paste & Run These
Create logical_operators.go:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
package main import "fmt" func main() { isAdult := true hasLicense := false hasPermit := true isRaining := false // ──────────────────────────────────────────────── // 1. Basic AND (&&) – both must be true // ──────────────────────────────────────────────── canDriveAlone := isAdult && hasLicense fmt.Printf("Can drive alone? %t (adult && license)\n", canDriveAlone) // false canDriveWithPermit := isAdult && hasPermit fmt.Printf("Can drive with permit? %t\n", canDriveWithPermit) // true // ──────────────────────────────────────────────── // 2. Basic OR (||) – at least one true // ──────────────────────────────────────────────── canEnterClub := isAdult || hasPermit fmt.Printf("Can enter club? %t (adult || permit)\n", canEnterClub) // true isNiceWeather := !isRaining fmt.Printf("Nice weather? %t (!raining)\n", isNiceWeather) // true // ──────────────────────────────────────────────── // 3. Combining && and || – precedence & parentheses // ──────────────────────────────────────────────── // && has higher precedence than || canGoOut := isAdult && hasPermit || hasLicense fmt.Printf("canGoOut (no paren): %t\n", canGoOut) // true // Use parentheses for clarity — always recommended canGoOutSafe := (isAdult && hasPermit) || hasLicense fmt.Printf("canGoOut (with paren): %t\n", canGoOutSafe) // ──────────────────────────────────────────────── // 4. Short-circuit evaluation – VERY IMPORTANT feature // ──────────────────────────────────────────────── var ptr *int = nil // This is SAFE because of short-circuit if ptr != nil && *ptr > 0 { fmt.Println("Pointer is non-nil and positive") } else { fmt.Println("Safe check passed without panic") } // This would CRASH without short-circuit // if *ptr > 0 { ... } // nil pointer dereference panic! // Another common pattern username := "" if username != "" && len(username) >= 3 { fmt.Println("Valid username") } else { fmt.Println("Invalid or empty username") } } |
4. The Most Important Feature: Short-Circuit Evaluation
This is why Go programmers love && and ||:
- && stops evaluating as soon as it finds a false
- || stops evaluating as soon as it finds a true
Real-world safety patterns you will use every day:
|
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
// Safe nil check if user != nil && user.IsActive() { ... } // Safe map lookup + value check value, ok := m[key] if ok && value > 0 { ... } // Safe file open + read file, err := os.Open("config.json") if err == nil && file != nil { // read file } |
Without short-circuit, the second part would be evaluated even when the first is false → panic or wrong result.
5. Precedence & Parentheses
- && has higher precedence than ||
- Always use parentheses when mixing them — makes code much clearer
|
0 1 2 3 4 5 6 7 8 |
// These two are the same — but second is easier to read a || b && c // = a || (b && c) a || (b && c) // recommended style |
6. Common Beginner Mistakes
- Expecting Python-style chaining
|
0 1 2 3 4 5 6 7 8 9 10 |
// WRONG if 18 <= age <= 65 { } // compile error // Correct if age >= 18 && age <= 65 { } |
- Writing if !isError == true instead of if !isError
|
0 1 2 3 4 5 6 7 8 9 10 |
// Redundant & ugly if !hasError == true { ... } // Clean & idiomatic if !hasError { ... } |
- Forgetting short-circuit when writing safe checks
7. Quick Practice – Try These
Predict the result:
- true && false → ?
- false || true && false → ?
- !false || false → ?
- ptr != nil && *ptr == 42 (ptr = nil) → crashes or not?
- “Hyderabad” != “” && len(“Hyderabad”) > 5 → ?
Run them in a small program — which one surprised you?
Any part still confusing?
- Why no &&= / ||= compound assignment?
- How short-circuit interacts with function calls that have side effects?
- Logical operators with non-bool types (spoiler: not allowed)?
- Or ready for bitwise operators next?
Keep writing small if conditions with different combinations — logical operators control the flow of almost every interesting program you’ll write.
You’re doing really great — keep the momentum! 💪🇮🇳🚀
