Saturday, February 7, 2026

Flow Control

    Profile Pic of Akash AmanAkash Aman

    Updated: February 2026

    Conditional Logic

    The if Statement

    Concept: Executes a block of code only if a specific condition is true.

    • Behavior: In Go, parentheses () are omitted for the condition, but curly braces {} are mandatory, even for a single line of code.

    The Initialization Statement (Scope Control)

    • Behavior: Go allows you to declare and initialize a variable directly within the if statement before the condition is checked.
    go
    // 'err' is only accessible inside this if/else block
    if err := runTask(); err != nil {
        fmt.Println("Error occurred:", err)
        return
    }
    // fmt.Println(err) // COMPILER ERROR: err is undefined here
    
    • Why: This prevents "Variable Pollution." By limiting a variable's life to only where it’s needed, you avoid accidentally using stale or incorrect data later in the function.

    The switch Statement

    Concept: A streamlined alternative to writing multiple if-else chains.

    • Behavior: Unlike languages like C++ or Java, Go has Implicit Break. It executes only the matching case and exits automatically. You don't need to write break at the end of every block.
    go
    switch day := "Tuesday"; day {
    case "Monday":
        fmt.Println("Start of the week")
    case "Tuesday", "Wednesday": // Combine multiple values
        fmt.Println("Mid-week")
    default:
        fmt.Println("Just another day")
    }
    

    The "Expressionless" Switch

    Behavior: If you don't provide a variable to the switch, it acts like a clean if-else if ladder where each case is its own boolean evaluation.

    go
    num := 42
    switch {
    case num < 0:
        fmt.Println("Negative")
    case num > 0:
        fmt.Println("Positive")
    }
    

    Iteration (Loops)

    The for Loop

    Concept: Go’s only looping construct. It is a "Swiss Army Knife" that replaces for, while, and do-while from other languages.

    Setup:

    • Standard: for i := 0; i < 10; i++ { ... }
    • While-style: for condition { ... }
    • Infinite: for { ... }

    Iterating with range

    Concept: The range keyword is the standard way to walk through collections like Slices, Maps, or Strings.

    Crucial Warning: The "Copy" Trap

    • Behavior: When you use range, it returns a copy of the element, not a pointer to the original. Modifying the iteration variable does not change the data inside the original slice or map.
    go
    primes := []int{2, 3, 5}
    
    for index, value := range primes {
        // value is a COPY of primes[index]
        fmt.Printf("At index %d, the prime is %d\n", index, value)
    }
    

    Iterating over Maps vs. Strings

    • Maps: Iteration is unordered. You may get a different sequence every time you run the loop.
    • Strings: Iteration is Unicode-aware. It decodes UTF-8 bytes into runes (characters) automatically.

    Examples

    Range over Slices & Arrays

    Concept: Iterating through a linear sequence of data.

    • Returns: (index int, value T)
    • Behavior: Returns two values: the index (int) and the value (the data at that index).
    go
    primes := []int{2, 3, 5, 7}
    
    for i, v := range primes {
        fmt.Printf("Index: %d, Value: %d\n", i, v)
    }
    
    // Pro-tip: Use an underscore (_) to ignore the index if you don't need it
    for _, v := range primes {
        fmt.Println("Just the value:", v)
    }
    

    Range over Maps

    Concept: Iterating through key-value pairs.

    • Returns: (key K, value V)
    • Behavior: Returns the key and the value. However, unlike slices, maps are unordered.

    Why: Go intentionally randomizes map iteration order to prevent developers from relying on a specific sequence, which could lead to fragile code that breaks if the underlying map implementation changes.

    go
    scores := map[string]int{"Akash": 90, "Alice": 95}
    
    for name, score := range scores {
        fmt.Printf("%s scored %d points\n", name, score)
    }
    

    Range over Strings

    Concept: Iterating through text characters.

    • Returns: (index int, char rune)
    • Behavior: Strings in Go are technically slices of bytes. However, range is Unicode-aware. It automatically decodes UTF-8 bytes into runes (individual characters).
    go
    // The character 'ó' actually takes up 2 bytes
    for i, r := range "Gó" {
        fmt.Printf("Position %d: %c\n", i, r)
    }
    
    /* Output: 
    Position 0: G
    Position 1: ó (Note: the next index would be 3, not 2!)
    */
    

    Why: This ensures that when you loop through a string containing emojis or special characters, you don't accidentally "break" a character by reading only half of its bytes.

    Range over Channels

    Concept: A way to receive values from a channel until that channel is explicitly closed.

    • Return: (value T) — Only the data sent into the channel.
    • Setup: Unlike other types, range on a channel only returns one value (the data sent into the channel).
    go
    // This loop will keep waiting for data until the channel is closed
    for msg := range messages {
        fmt.Println("Received:", msg)
    }
    

    Behavior: The "Waiting" State

    • The loop is blocking. If the channel is empty but not closed, the loop will sit and wait for the next value to arrive. If you forget to close the channel, the loop will wait forever, potentially causing a "deadlock."

    Why: Clean Termination

    • Using range over a channel is the most idiomatic way to "drain" a channel. Instead of manually checking if a channel is open using val, ok := <-ch, the range loop automatically handles the exit logic for you the moment close(ch) is called.

    Behavioral Note: You must close the channel from the sender's side. If you don't, the range loop will never finish, and your program might crash or hang.

    Advanced Flow Control

    The defer Keyword

    Concept: Schedules a function to run immediately before the current function returns.

    • Why: It is used for "Cleanup Logic." It ensures that resources like files or database connections are closed properly, even if the program crashes (panics).
    go
    func processFile() {
        f, _ := os.Open("note.txt")
        defer f.Close() // This is guaranteed to run last
    
        // ... perform file operations ...
    }
    

    Behavior: Multiple defer statements follow a LIFO (Last-In, First-Out) order—the last one scheduled is the first one executed.

    Loop Control & Labels

    Concept: Keywords that modify how a loop behaves mid-execution.

    • break: Immediately exits the current loop.
    • continue: Skips the rest of the current iteration and starts the next one.

    The "Label" (Breaking Nested Loops)

    • Behavior: In nested loops, a standard break only exits the inner loop. A Label allows you to break out of the parent loop entirely.
    go
    MainLoop:
        for i := 0; i < 5; i++ {
            for j := 0; j < 5; j++ {
                if i*j > 10 {
                    break MainLoop // Exits both loops instantly
                }
            }
        }
    
    • Why: This avoids "Spaghetti Logic" where you would otherwise need multiple boolean flags to signal the outer loop to stop.
    KeywordScopeResult
    breakCurrent BlockStops the loop/switch and moves to the next code line.
    continueCurrent LoopSkips the current turn and starts the next iteration.
    break LABELLabeled ParentTerminates the specific parent loop associated with the label.

    © 2026 Akash Aman | All rights reserved