Post

03. Control Flow in Go

🚀 Master the art of directing your Go programs! This guide breaks down essential control flow mechanisms like if-else, switch, for loops, and more, enabling you to write efficient and decision-driven code. 🎯

03. Control Flow in Go

What we will learn in this post?

  • 👉 If-Else Statements
  • 👉 Switch Statements
  • 👉 For Loops
  • 👉 Range Keyword
  • 👉 Break and Continue
  • 👉 Goto and Labels
  • 👉 Conclusion!

Conditional Statements in Go 🚀

Let’s dive into how Go handles conditional logic! Go uses if, if-else, and if-else-if statements to control the flow of your program based on conditions. The best part? No parentheses needed around your conditions, but braces {} are mandatory.

if Statement 🤔

The if statement executes a block of code only if a condition is true.

1
2
3
4
5
6
7
8
9
10
11
package main

import "fmt"

func main() {
    age := 25

    if age >= 18 {
        fmt.Println("You are eligible to vote!")
    }
}

if-else Statement ⚖️

The if-else statement executes one block of code if the condition is true and another block if it’s false.

1
2
3
4
5
6
7
8
9
10
11
12
13
package main

import "fmt"

func main() {
    number := 7

    if number%2 == 0 {
        fmt.Println("Even number")
    } else {
        fmt.Println("Odd number")
    }
}

if-else-if Statement 🪜

The if-else-if statement lets you check multiple conditions in sequence.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
    score := 75

    if score >= 90 {
        fmt.Println("Excellent!")
    } else if score >= 70 {
        fmt.Println("Good!")
    } else {
        fmt.Println("Needs improvement")
    }
}

Short Statement Syntax 💫

Go provides a neat feature within if statements: the short statement. This allows you to declare and initialize a variable within the if condition itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
package main

import "fmt"

func main() {
    if x := getValue(); x > 0 {
        fmt.Println("Value is positive:", x)
    } else {
		fmt.Println("Value is not positive:", x)
	}
}

func getValue() int {
	return -5 // You can change this to test different values.
}

In this example, x := getValue() is executed before the condition x > 0 is evaluated. The variable x is only scoped to the if and else blocks.

  • Key Takeaways:
    • Go requires braces {}.
    • Go doesn’t need parentheses around conditions ().
    • The short statement syntax (if x := ...; condition) is handy!
graph TD
    A[Start] --> B{Condition 1?};
    B -- True --> C[Execute Block 1];
    B -- False --> D{Condition 2?};
    D -- True --> E[Execute Block 2];
    D -- False --> F[Execute Block 3];
    C --> G[End];
    E --> G;
    F --> G;

More Information Resources:

  1. Go if Statements on Go by Example
  2. Effective Go - Control Structures

Switch Statements in Go: A Simple Guide 🚦

Go’s switch statement is a clean way to handle multiple conditions. Think of it as a more powerful if-else if-else chain. The cool part? Go handles break for you!

Types of Switches ✨

Go provides different flavors of switch statements:

  • Expression Switch: Evaluates an expression and matches its value against case values.
1
2
3
4
5
6
7
8
9
grade := "B"
switch grade {
case "A":
    println("Excellent!")
case "B":
    println("Good job!")
default:
    println("Keep trying!")
}
  • Type Switch: Used to determine the type of a variable. Super handy when dealing with interface{}.
1
2
3
4
5
6
7
8
9
var i interface{} = 10
switch v := i.(type) {
case int:
    println("It's an integer:", v)
case string:
    println("It's a string:", v)
default:
    println("I don't know what it is!")
}

fallthrough Keyword 🍂

Normally, Go executes only the matching case and then automatically exits the switch. The fallthrough keyword makes execution continue to the next case, regardless of whether it matches.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
num := 2
switch num {
case 1:
    println("One")
case 2:
    println("Two")
    fallthrough
case 3:
    println("Three")
default:
    println("Something else")
}
// Output:
// Two
// Three

Implicit break Behavior 👍

Go implicitly adds a break statement after each case. You don’t need to write break yourself. This helps prevent accidental fall-through bugs common in other languages.

Switch with no Expression 🔍

You can also write switch statements without an expression. This is similar to using a chain of if/else if statements.

1
2
3
4
5
6
7
8
9
age := 25
switch {
case age < 18:
    println("Minor")
case age >= 18 && age < 65:
    println("Adult")
default:
    println("Senior")
}

Check out the official Go documentation here for even more details! 📖

Go’s Looping Powerhouse: The for Loop 🔄

Go keeps things simple! Instead of multiple loop types like while or do-while in other languages, Go rocks just one: the for loop. But don’t let that fool you; it’s super flexible and can handle all your looping needs.

Three Flavors of for 🍦

Traditional Three-Component for ⚙️

Like in C or Java, you’ve got initialization, condition, and post-statement, all in one line.

1
2
3
for i := 0; i < 5; i++ {
    println(i) // Prints 0 to 4
}
  • Use Case: Looping a specific number of times, like processing elements in a slice or array.
    graph LR
    A[Start] --> B{i := 0};
    B --> C{i < 5};
    C -- True --> D{println(i)};
    D --> E{i++};
    E --> C;
    C -- False --> F[End];
    

    While-Style for

Just a condition, like a while loop.

1
2
3
4
5
i := 0
for i < 5 {
    println(i)
    i++
}
  • Use Case: Looping until a condition is met, perhaps reading from a stream or waiting for an event.

Infinite Loop ♾️

No condition at all! Keep going until you break out.

1
2
3
4
5
6
for {
    // Do something
    if someCondition {
        break // Exit the loop
    }
}
  • Use Case: Servers listening for incoming connections, or background processes running continuously.

You can find more information about Go for loops in the official Go documentation.

The Magic of range in Go 🪄

The range keyword in Go is your friendly iterator! It makes looping through data structures easy peasy. Think of it as a helper that automatically gives you both the index and the value of each item.

range across Different Data Structures 🧰

Let’s explore how range works with various data types:

  • Arrays and Slices:
    1
    2
    3
    4
    
    numbers := []int{10, 20, 30}
    for index, value := range numbers {
        fmt.Printf("Index: %d, Value: %d\n", index, value)
    }
    

    Here, index represents the position (0, 1, 2) and value is the element at that position.

  • Maps:

    1
    2
    3
    4
    
    ages := map[string]int{"Alice": 30, "Bob": 25}
    for name, age := range ages {
        fmt.Printf("Name: %s, Age: %d\n", name, age)
    }
    

    name becomes the key, and age is the value associated with that key.

  • Strings:

    1
    2
    3
    4
    
    word := "Hello"
    for index, runeValue := range word {
        fmt.Printf("Index: %d, Rune: %c\n", index, runeValue)
    }
    

    Important Note: range on strings iterates over runes, which are Unicode code points (like characters).

  • Channels:

    1
    2
    3
    4
    5
    6
    7
    8
    
    ch := make(chan int, 3)
    ch <- 1
    ch <- 2
    close(ch) // important for channels
    
    for value := range ch {
        fmt.Println("Received:", value)
    }
    

    range receives values from the channel until it’s closed.

Ignoring Values with _ 🤫

Sometimes, you only need the index (or the key) and not the value. Use the _ (underscore) to ignore the value:

1
2
3
for index, _ := range numbers { // ignores the value
    fmt.Println("Index:", index)
}

More info

range simplifies looping, providing a clean and efficient way to iterate through Go’s core data structures!

Loop Control: Break & Continue 🚀

Let’s explore how to fine-tune your loops with break and continue!

Breaking Free & Skipping Ahead

  • break: Think of break as an emergency exit. It immediately terminates the loop’s execution, even if the loop condition is still true.

    1
    2
    3
    4
    
    for i in range(5):
        if i == 3:
            break  # Exit the loop when i is 3
        print(i) # output: 0, 1, 2
    
  • continue: continue is like a “fast forward” button. It skips the current iteration and jumps to the next one.

    1
    2
    3
    4
    
    for i in range(5):
        if i == 2:
            continue  # Skip iteration when i is 2
        print(i) # output: 0, 1, 3, 4
    

Labeled Break & Continue

  • For nested loops, you can use labels with break and continue to specify which loop you want to affect.

    1
    2
    3
    4
    5
    
    outer_loop: for i in range(3):
        for j in range(3):
            if i == 1 and j == 1:
                break outer_loop  # Exit the outer loop
            print(f"i={i}, j={j}")
    

Here is a flow chart explaining break and continue statement.

graph TD
    A[Start Loop] --> B{Condition};
    B -- True --> C{Code};
    C --> D{Break Statement?};
    D -- Yes --> E[Exit Loop];
    D -- No --> F{Continue Statement?};
    F -- Yes --> B;
    F -- No --> G[Execute Rest of Loop];
    G --> B;
    B -- False --> E;
    E --> H[End];

Practical Example: Searching in a 2D grid. If we find the target, we want to stop the entire search.

goto Statements and Labels in Go 🚀

goto in Go jumps execution to a labeled point within a function. Labels are markers in your code, like errHandler: before a block.

When goto Can Be Helpful

  • Error Handling: Quickly jump to a central error handling block.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    func myFunc() error {
        // ... some code
        if err != nil {
            goto errHandler // Jump to error handling
        }
        // ... more code
    errHandler:
        return fmt.Errorf("error occurred: %w", err)
    }
    
  • Breaking Nested Loops: Exit multiple nested loops efficiently.

    1
    2
    3
    4
    5
    6
    7
    8
    
    loop:
        for i := 0; i < 3; i++ {
            for j := 0; j < 3; j++ {
                if i*j > 5 {
                    goto loop // Exit both loops
                }
            }
        }
    

Why Use Sparingly?

goto can make code harder to read and understand. Too many goto statements can create what is commonly referred to as “spaghetti code”. This makes debugging and maintaining the code much more difficult.

  • It complicates control flow.
  • It reduces code readability.

Best Practice: Prefer structured control flow (if/else, loops, functions). Use goto only when necessary and with extreme caution.

For more info you can check the official documentation on Go’s control flow.

Okay, here are a few options for your conclusion statement, formatted as requested:

Option 1:

Conclusion

Well, that’s a wrap! 🎉 I hope you enjoyed reading this post and found it helpful. Now it’s your turn! What are your thoughts? Any tips or tricks you’d like to share? Let me know in the comments below! 👇 I’m really looking forward to hearing from you. 😊

Option 2:

Conclusion

And there you have it! ✨ I’m curious to know if you have any experiences related to this topic. Or maybe some suggestions for improvement? 🧐 Don’t be shy - share your insights in the comment section! I’m always open to new ideas and learning from you all. Thanks for reading! 👍

Option 3:

Conclusion

Alright, folks, we’ve reached the end! 🥳 But the conversation doesn’t have to stop here! What are your takes on this? Did I miss anything important? 🤔 Drop a comment below and let’s chat! I value your feedback and suggestions. Happy commenting! ✍️

This post is licensed under CC BY 4.0 by the author.