Go Basics: Flow control
Abhinav Pandey
Posted on December 9, 2021
Flow Control
In the last article, we saw how to use functions. We also saw a preview of the for
loop. We will explore the flow control syntax in depth in this article.
Go has the following flow control mechanisms:
-
if/else
-
switch
-
for
-
defer
If you are familiar with other languages, defer
might be new to you and you may be missing while
loops in this list.
Let's look at the syntax of each one of them.
Contents
If and else
What's similar to other languages:
if condition {
// execute if condition is true
} else if condition {
// execute if first condition is false but the second is true
} else {
// execute if both conditions are false
}
Notice that there are no braces around the conditions.
What's different from other languages:
if a:=rand.Intn(2); a==1 {
return a
} else {
return a+1
}
You can declare a variable before the condition and use it in the condition or in the if/else blocks.
Switch
Switch in Go is much more powerful than in other languages.
A simple swtich/case
switch a {
case 1:
fmt.Println("a is 1")
case 2:
fmt.Println("a is 2")
default:
fmt.Println("a is neither 1 nor 2")
}
The switch
statement is used to execute different blocks of code depending on the value of a variable. Again, no braces around the value.
And if you've been coding in other languages (like Java), having no break
must be a welcome change. There is no fall through in Go and only the first matching case is executed.
More flexible switch/case
Just like if/else, you can also declare a variable before the switch and use it in the switch/case blocks.
switch a := rand.Intn(10); a {
case 0, 1, 2, 3, 4:
fmt.Println("a is 0, 1, 2, 3 or 4")
case 5, 6, 7, 8, 9:
fmt.Println("a is 5, 6, 7, 8 or 9")
default:
fmt.Println("a is neither 0, 1, 2, 3, 4, 5, 6, 7, 8 or 9")
}
Also notice how a case can be matched with multiple values. In Java, this was achieved with fall through.
Not just integers
You can also use switch case with strings, other types, and even expressions.
switch name {
case "John", "Paul", "George", "Ringo":
fmt.Println("str is one of John, Paul, George or Ringo")
}
//switch/case with expression
switch time.Now().Weekday() {
case time.Saturday, time.Sunday:
fmt.Println("It's the weekend")
default:
fmt.Println("It's a weekday")
}
This means that now you can use the switch
statement for any type. Also notice that default
is not mandatory.
Switch without a condition
You can also use the switch
statement without a condition. Rather you can put your condition in the case
block.
switch {
case a > b:
fmt.Println("a is greater than b")
case a < b:
fmt.Println("a is less than b")
default:
fmt.Println("a is equal to b")
}
This makes switch
even more powerful because this construct is similar to else if ladders.
However, it does not give a performance boost because it will not use a jump table. It is only good for readability.
For loops
We saw an example of the for
loop in the last article. Now let's look closer at the syntax.
A simple for
for i := 0; i < 10; i++ {
fmt.Println(i)
}
The for
loop is used to execute a block of code a number of times. The above construct has almost no difference from the for
loop in other languages.
The only difference is the shorthand variable declaration of the loop variable. I'm sure you are an expert with the shorthand variable declaration by now. It's everywhere.
All parts of the looping construct are optional
Infinite loops
// infinite loop
for {
fmt.Println("loop")
}
Optional update
for i := 0; i < 10; {
i++;
fmt.Println("loop")
}
Optional initialization
i:=0
for ; i < 10; i++ {
fmt.Println("loop")
}
Without initialization or update
i:=0
for ;i < 10; {
i++
fmt.Println("loop")
}
Don't even use the semicolons if its just a condition
i:=0
for i<10 {
i++
fmt.Println("loop")
}
Wait a minute, isn't this a while loop? - YES
And that's why there is no explicit while
loop in Go.
for with range
for i, v := range []int{1, 3, 5} {
fmt.Println(i, v)
}
The range
statement is used to iterate over a slice or map. Here we iterate over a slice of integers.
We will look into the array syntax later but for now focus on the variables.
-
i
is the index of the element in the slice -
v
is the value of the element in the slice So the above construct will print0 1
and1 3
and2 5
.
That's it about the for
loop. Let's move on to something new now.
defer
The defer
statement is used to execute a block of code at the end of the function.
This is sometimes called a "last resort" or "finalizer" because it is executed after the function returns.
A simple defer
func main() {
defer fmt.Println("world")
fmt.Println("hello")
}
The above example will print hello
and then world
at the end of the function. It does not matter where the defer
statement is placed. It will always be executed at the end of the function.
This must be a little strange but it is useful in many scenarios. For example:
- When you want to close a file or database connection.
- Logging the end of a function.
- Logging what values a variable has at the end of a function.
There are many other examples of defer
in the Go language. You can read more about it here.
One point worth mentioning is that the defer
statement is executed in LIFO order. If you have multiple defer
statements, the last one will be executed first.
Let's see an example:
func main() {
fmt.Println("counting")
for i := 0; i < 10; i++ {
defer fmt.Println(i)
}
fmt.Println("done")
}
This will print counting
and then done
and then 9
and then 8
and so on. Similar to a stacktrace.
This should give you an idea about flow control in Go. In the next article, I will cover more syntactical elements of Go.
Thanks for reading. If you want to connect with me, you can find me on Twitter @abh1navv.
Posted on December 9, 2021
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.