Skip to content

Go Conditionals: If-Else and Switch Mastery

Overview

Master Go's conditional statements for intelligent decision-making in your programs. This comprehensive guide covers if-else statements, switch statements, and advanced conditional patterns, building upon our understanding of loops and data types.

Key Points

  • Clean and readable conditional syntax
  • Powerful switch statements with multiple patterns
  • Short variable declarations in conditions
  • Type switches for interface handling
  • Fallthrough control in switch cases

Understanding Conditional Logic 🤔

Conditional statements are the decision-making backbone of any program. Go provides elegant and powerful constructs for handling different execution paths.

Conditional Constructs in Go

graph TD
    A[Go Conditionals] --> B[If-Else]
    A --> C[Switch]
    B --> B1[Basic If]
    B --> B2[If-Else]
    B --> B3[If-Else If]
    B --> B4[Short Declaration]
    C --> C1[Expression Switch]
    C --> C2[Type Switch]
    C --> C3[No Expression]
    C --> C4[Fallthrough]
    style A fill:#999,stroke:#333,stroke-width:2px,color:#000

If-Else Statements: Basic Decision Making 🍴

The if-else family provides straightforward conditional execution based on boolean expressions.

Basic If Statement

Simple If Statements

basic_if.go
package main

import "fmt"

func main() {
    temperature := 25

    if temperature > 20 {
        fmt.Println("It's a warm day!")
    }

    // Output: It's a warm day!
}

Boolean Conditions

The condition must evaluate to a boolean value (true or false).

multiple_conditions.go
package main

import "fmt"

func main() {
    age := 25
    hasLicense := true

    if age >= 18 && hasLicense {
        fmt.Println("You can drive!")
    }

    // Output: You can drive!
}
comparisons.go
package main

import "fmt"

func main() {
    score := 85

    if score >= 90 {
        fmt.Println("Grade: A")
    }

    if score >= 80 && score < 90 {
        fmt.Println("Grade: B")
    }

    if score < 60 {
        fmt.Println("Grade: F")
    }

    // Output: Grade: B
}

If-Else Statement

If-Else Patterns

if_else.go
package main

import "fmt"

func main() {
    number := 7

    if number%2 == 0 {
        fmt.Printf("%d is even\n", number)
    } else {
        fmt.Printf("%d is odd\n", number)
    }

    // Output: 7 is odd
}
if_else_if.go
package main

import "fmt"

func main() {
    grade := 85

    if grade >= 90 {
        fmt.Println("Excellent!")
    } else if grade >= 80 {
        fmt.Println("Good job!")
    } else if grade >= 70 {
        fmt.Println("Not bad!")
    } else {
        fmt.Println("Keep trying!")
    }

    // Output: Good job!
}
complex_conditions.go
package main

import "fmt"

func main() {
    age := 25
    income := 50000
    creditScore := 720

    if age >= 18 && income >= 30000 && creditScore >= 650 {
        fmt.Println("Loan approved!")
    } else if age < 18 {
        fmt.Println("Must be 18 or older")
    } else if income < 30000 {
        fmt.Println("Insufficient income")
    } else {
        fmt.Println("Credit score too low")
    }

    // Output: Loan approved!
}

Short Variable Declaration in If

Go allows you to declare and initialize variables within if statements, creating a limited scope.

Short Declaration in If

short_declaration.go
package main

import "fmt"

func main() {
    // Variable declared in if statement
    if x := 10; x > 5 {
        fmt.Printf("x is %d and greater than 5\n", x)
    }
    // x is not accessible here

    // Output: x is 10 and greater than 5
}

Scope Limitation

Variables declared in if statements are only accessible within the if-else block.

error_handling.go
package main

import (
    "fmt"
    "strconv"
)

func main() {
    input := "123"

    if num, err := strconv.Atoi(input); err != nil {
        fmt.Printf("Error converting '%s': %v\n", input, err)
    } else {
        fmt.Printf("Successfully converted to %d\n", num)
    }

    // Output: Successfully converted to 123
}
function_call.go
package main

import "fmt"

func getTemperature() int {
    return 25
}

func main() {
    // Call function and use result in condition
    if temp := getTemperature(); temp > 20 {
        fmt.Printf("Temperature is %d°C - Nice weather!\n", temp)
    } else {
        fmt.Printf("Temperature is %d°C - A bit cold\n", temp)
    }

    // Output: Temperature is 25°C - Nice weather!
}

Switch Statements: Multi-Way Branching 🔄

Switch statements provide an elegant way to handle multiple conditions and are more readable than long if-else chains.

Expression Switch

Basic Switch Statements

simple_switch.go
package main

import "fmt"

func main() {
    day := "Monday"

    switch day {
    case "Monday":
        fmt.Println("Start of the work week")
    case "Friday":
        fmt.Println("TGIF!")
    case "Saturday", "Sunday":
        fmt.Println("Weekend!")
    default:
        fmt.Println("Regular weekday")
    }

    // Output: Start of the work week
}
switch_expression.go
package main

import "fmt"

func main() {
    score := 85

    switch {
    case score >= 90:
        fmt.Println("Grade: A")
    case score >= 80:
        fmt.Println("Grade: B")
    case score >= 70:
        fmt.Println("Grade: C")
    case score >= 60:
        fmt.Println("Grade: D")
    default:
        fmt.Println("Grade: F")
    }

    // Output: Grade: B
}
switch_short_declaration.go
package main

import (
    "fmt"
    "time"
)

func main() {
    switch hour := time.Now().Hour(); {
    case hour < 12:
        fmt.Println("Good morning!")
    case hour < 17:
        fmt.Println("Good afternoon!")
    case hour < 21:
        fmt.Println("Good evening!")
    default:
        fmt.Println("Good night!")
    }
}

Advanced Switch Patterns

Advanced Switch Usage

multiple_values.go
package main

import "fmt"

func main() {
    char := 'a'

    switch char {
    case 'a', 'e', 'i', 'o', 'u':
        fmt.Printf("'%c' is a vowel\n", char)
    case 'y':
        fmt.Printf("'%c' is sometimes a vowel\n", char)
    default:
        fmt.Printf("'%c' is a consonant\n", char)
    }

    // Output: 'a' is a vowel
}
fallthrough.go
package main

import "fmt"

func main() {
    number := 2

    switch number {
    case 1:
        fmt.Println("One")
        fallthrough
    case 2:
        fmt.Println("Two or after One")
        fallthrough
    case 3:
        fmt.Println("Three or after Two")
    default:
        fmt.Println("Other number")
    }

    // Output:
    // Two or after One
    // Three or after Two
}
function_results.go
package main

import "fmt"

func classify(n int) string {
    if n%2 == 0 {
        return "even"
    }
    return "odd"
}

func main() {
    number := 7

    switch classify(number) {
    case "even":
        fmt.Printf("%d is an even number\n", number)
    case "odd":
        fmt.Printf("%d is an odd number\n", number)
    default:
        fmt.Printf("Unknown classification for %d\n", number)
    }

    // Output: 7 is an odd number
}

Type Switch

Type switches are a powerful feature for working with interfaces and determining the actual type of a value.

Type Switch Patterns

type_switch.go
package main

import "fmt"

func describe(i interface{}) {
    fmt.Printf("Value: %v, ", i)

    switch v := i.(type) {
    case int:
        fmt.Printf("Type: int, Double: %d\n", v*2)
    case string:
        fmt.Printf("Type: string, Length: %d\n", len(v))
    case bool:
        fmt.Printf("Type: bool, Negated: %t\n", !v)
    default:
        fmt.Printf("Type: %T (unknown)\n", v)
    }
}

func main() {
    describe(42)
    describe("hello")
    describe(true)
    describe(3.14)

    // Output:
    // Value: 42, Type: int, Double: 84
    // Value: hello, Type: string, Length: 5
    // Value: true, Type: bool, Negated: false
    // Value: 3.14, Type: float64 (unknown)
}
multiple_types.go
package main

import "fmt"

func processValue(i interface{}) {
    fmt.Printf("Processing: %v -> ", i)

    switch v := i.(type) {
    case int, int32, int64:
        fmt.Printf("Integer type: %T\n", v)
    case float32, float64:
        fmt.Printf("Float type: %T\n", v)
    case string:
        fmt.Printf("String with %d characters\n", len(v))
    case []int:
        fmt.Printf("Integer slice with %d elements\n", len(v))
    case nil:
        fmt.Println("Nil value")
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}

func main() {
    processValue(42)
    processValue(3.14)
    processValue("Go")
    processValue([]int{1, 2, 3})
    processValue(nil)
}

Advanced Conditional Patterns ⚙

Conditional Expressions and Operators

Logical Operators and Complex Conditions

logical_operators.go
package main

import "fmt"

func main() {
    age := 25
    hasJob := true
    creditScore := 750

    // AND operator
    if age >= 18 && hasJob && creditScore >= 700 {
        fmt.Println("Eligible for premium loan")
    }

    // OR operator
    if age < 18 || creditScore < 600 {
        fmt.Println("Not eligible for loan")
    } else {
        fmt.Println("Basic loan eligibility met")
    }

    // NOT operator
    if !(age < 18) {
        fmt.Println("Adult")
    }

    // Output:
    // Eligible for premium loan
    // Basic loan eligibility met
    // Adult
}
short_circuit.go
package main

import "fmt"

func expensiveCheck() bool {
    fmt.Println("Expensive check called")
    return true
}

func main() {
    condition := false

    // expensiveCheck() won't be called due to short-circuit
    if condition && expensiveCheck() {
        fmt.Println("Both conditions true")
    }

    // expensiveCheck() will be called
    if !condition || expensiveCheck() {
        fmt.Println("At least one condition true")
    }

    // Output:
    // Expensive check called
    // At least one condition true
}

Nested Conditionals

Nested Conditional Patterns

nested_conditionals.go
package main

import "fmt"

func main() {
    weather := "sunny"
    temperature := 25

    if weather == "sunny" {
        if temperature > 20 {
            fmt.Println("Perfect day for outdoor activities!")
        } else {
            fmt.Println("Sunny but a bit cold")
        }
    } else if weather == "rainy" {
        if temperature > 15 {
            fmt.Println("Warm rain - good for plants")
        } else {
            fmt.Println("Cold and rainy - stay inside")
        }
    } else {
        fmt.Println("Weather conditions unclear")
    }

    // Output: Perfect day for outdoor activities!
}
guard_clauses.go
package main

import "fmt"

func processUser(name string, age int, email string) {
    // Guard clauses for early returns
    if name == "" {
        fmt.Println("Error: Name cannot be empty")
        return
    }

    if age < 0 || age > 150 {
        fmt.Println("Error: Invalid age")
        return
    }

    if email == "" {
        fmt.Println("Error: Email cannot be empty")
        return
    }

    // Main processing logic
    fmt.Printf("Processing user: %s, age %d, email %s\n", name, age, email)
}

func main() {
    processUser("Alice", 25, "alice@example.com")
    processUser("", 30, "bob@example.com")
    processUser("Charlie", -5, "charlie@example.com")
}

Best Practices 📓

Conditional Best Practices

  1. Prefer Switch Over Long If-Else Chains

    // Good: Use switch for multiple discrete values
    switch status {
    case "pending", "processing":
        handleInProgress()
    case "completed":
        handleCompleted()
    case "failed":
        handleFailed()
    }
    
    // Avoid: Long if-else chains
    if status == "pending" || status == "processing" {
        handleInProgress()
    } else if status == "completed" {
        handleCompleted()
    } else if status == "failed" {
        handleFailed()
    }
    

  2. Use Short Variable Declaration for Error Handling

    1
    2
    3
    4
    5
    if result, err := someOperation(); err != nil {
        return err
    } else {
        return result
    }
    

  3. Leverage Type Switches for Interface Handling

    1
    2
    3
    4
    5
    6
    7
    8
    switch v := value.(type) {
    case string:
        return processString(v)
    case int:
        return processInt(v)
    default:
        return processUnknown(v)
    }
    

  4. Use Guard Clauses for Early Returns

    func validate(input string) error {
        if input == "" {
            return errors.New("input cannot be empty")
        }
        if len(input) > 100 {
            return errors.New("input too long")
        }
        // Continue with main logic
        return nil
    }
    

Common Pitfalls

  • Missing Default Cases: Always consider adding default cases in switch statements
  • Complex Conditions: Break down complex boolean expressions for readability
  • Deep Nesting: Avoid deeply nested conditionals; use guard clauses instead
  • Fallthrough Confusion: Be explicit about fallthrough behavior in switch statements

Performance Considerations ⚡

Optimization Tips

performance_comparison.go
package main

import "fmt"

func processWithSwitch(value int) string {
    // Generally faster for many discrete values
    switch value {
    case 1, 2, 3:
        return "low"
    case 4, 5, 6:
        return "medium"
    case 7, 8, 9:
        return "high"
    default:
        return "unknown"
    }
}

func processWithIfElse(value int) string {
    // Better for range conditions
    if value >= 1 && value <= 3 {
        return "low"
    } else if value >= 4 && value <= 6 {
        return "medium"
    } else if value >= 7 && value <= 9 {
        return "high"
    } else {
        return "unknown"
    }
}

func main() {
    fmt.Println(processWithSwitch(5))
    fmt.Println(processWithIfElse(5))
}
condition_ordering.go
package main

import "fmt"

func checkConditions(value int) string {
    // Order conditions by likelihood (most common first)
    if value >= 0 && value <= 100 {  // Most common case
        return "normal range"
    } else if value < 0 {            // Less common
        return "negative"
    } else {                         // Least common
        return "above range"
    }
}

func main() {
    fmt.Println(checkConditions(50))
}

Quick Reference 📑

Key Takeaways

  1. If-Else: Use for boolean conditions and ranges
  2. Switch: Prefer for discrete values and type checking
  3. Short Declaration: Leverage for scoped variables in conditions
  4. Type Switch: Essential for interface type determination
  5. Guard Clauses: Use for early returns and validation
  6. Performance: Consider condition ordering and switch vs if-else trade-offs

Remember

"Choose the right conditional construct for the job. Switch for discrete values, if-else for ranges and complex boolean logic, and type switches for interface handling."