Conditional Logic

Overview

Go provides if/else statements and switch statements for conditional logic. Both are straightforward but have unique features compared to other languages.

The if Statement

Basic Syntax

if condition {
    // code
}

With else

if x > 0 {
    fmt.Println("Positive")
} else {
    fmt.Println("Non-positive")
}

With else if

if x > 0 {
    fmt.Println("Positive")
} else if x < 0 {
    fmt.Println("Negative")
} else {
    fmt.Println("Zero")
}

Initialization Statement

Go allows a statement before the condition:

if err := doSomething(); err != nil {
    // Handle error
    // err is only visible in this if/else block
    return err
}
// err is not accessible here

This is idiomatic for error handling:

if file, err := os.Open("data.txt"); err != nil {
    log.Fatal(err)
} else {
    defer file.Close()
    // Use file
}

No Parentheses Required

Unlike C-style languages, conditions don’t need parentheses:

// Go style
if x > 0 {
}

// Not idiomatic (but valid)
if (x > 0) {
}

Braces Required

Braces are mandatory, even for single statements:

// Error: missing braces
if x > 0
    fmt.Println("yes")

// Correct
if x > 0 {
    fmt.Println("yes")
}

Boolean Expressions

Comparison Operators

Operator Meaning
== Equal
!= Not equal
< Less than
> Greater than
<= Less than or equal
>= Greater than or equal

Logical Operators

Operator Meaning
&& AND (short-circuit)
|| OR (short-circuit)
! NOT
// Short-circuit evaluation
if x != 0 && y/x > 1 {
    // y/x only evaluated if x != 0
}

if ptr != nil && ptr.Value > 0 {
    // Ptr.Value only accessed if ptr != nil
}

The switch Statement

Basic Switch

switch day {
case "Monday":
    fmt.Println("Start of week")
case "Friday":
    fmt.Println("Almost weekend!")
case "Saturday", "Sunday":
    fmt.Println("Weekend!")
default:
    fmt.Println("Midweek")
}

No Fall-Through by Default

Unlike C, cases don’t fall through:

switch n {
case 1:
    fmt.Println("One")
    // No break needed, stops here
case 2:
    fmt.Println("Two")
}

Explicit Fallthrough

Use fallthrough keyword when needed:

switch n {
case 1:
    fmt.Println("One")
    fallthrough
case 2:
    fmt.Println("Two")
}
// Input: 1
// Output:
// One
// Two

Switch with Initialization

switch os := runtime.GOOS; os {
case "darwin":
    fmt.Println("macOS")
case "linux":
    fmt.Println("Linux")
default:
    fmt.Println(os)
}

Expression-less Switch

A switch without an expression is equivalent to switch true:

t := time.Now()

switch {
case t.Hour() < 12:
    fmt.Println("Good morning!")
case t.Hour() < 17:
    fmt.Println("Good afternoon!")
default:
    fmt.Println("Good evening!")
}

This is often cleaner than chained if/else if:

switch {
case x < 0:
    return "negative"
case x == 0:
    return "zero"
case x > 0:
    return "positive"
}

Type Switch

Special form for interface type assertions:

func describe(i interface{}) {
    switch v := i.(type) {
    case int:
        fmt.Printf("Integer: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    case bool:
        fmt.Printf("Boolean: %t\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

describe(42)        // Integer: 42
describe("hello")   // String: hello
describe(true)      // Boolean: true
describe(3.14)      // Unknown type: float64

Common Patterns

Error Checking

result, err := someOperation()
if err != nil {
    return fmt.Errorf("operation failed: %w", err)
}
// Use result

Boolean Assignment

var status string
if success {
    status = "completed"
} else {
    status = "failed"
}

// Note: Go doesn't have a ternary operator
// This doesn't work: status := success ? "completed" : "failed"

Guard Clauses

func processUser(user *User) error {
    if user == nil {
        return errors.New("user cannot be nil")
    }
    if user.Name == "" {
        return errors.New("user name required")
    }
    if user.Age < 0 {
        return errors.New("invalid age")
    }

    // Main logic here
    return nil
}

ok-idiom

// Map lookup
if val, ok := myMap[key]; ok {
    fmt.Println("Found:", val)
} else {
    fmt.Println("Not found")
}

// Type assertion
if str, ok := value.(string); ok {
    fmt.Println("String:", str)
}

// Channel receive
if val, ok := <-ch; ok {
    fmt.Println("Received:", val)
} else {
    fmt.Println("Channel closed")
}

Comma-ok Pattern

// Check map existence
if _, exists := users[id]; exists {
    // User exists
}

// Check channel state
if _, open := <-ch; !open {
    // Channel is closed
}

Switch vs If/Else

Prefer Switch When:

// Multiple discrete values
switch status {
case "pending", "processing":
    queue()
case "done":
    complete()
case "error":
    retry()
}

Prefer If/Else When:

// Complex conditions
if x > 0 && y > 0 && x*y < 100 {
    // ...
} else if x < 0 || y < 0 {
    // ...
}

Summary

Feature Description
if Basic conditional
if x := val; condition If with initialization
switch expr Multi-way branching
switch { case cond: } Expression-less switch
switch v := x.(type) Type switch
fallthrough Explicit fall-through in switch