Control Flows

The best part of programming in Go is that there are not multiple ways of doing standard things and that makes reading code and understanding it easier. But at the same time as we saw in previous post Go follows different conventions which needs to be understood.

for Loop

The for loop construct, is the only way for looping. Unlike other languages Go does not have familiar constructs like while, do-while etc. Let us look at the traditional for loop

func main() {
	for i := 0; i < 10; i++ {
		fmt.Println("Value of i =", i)
	}
}

http://play.golang.org/p/bo7OjUCcBE

Output:

Value of i = 0
Value of i = 1
Value of i = 2
Value of i = 3
Value of i = 4
Value of i = 5
Value of i = 6
Value of i = 7
Value of i = 8
Value of i = 9

An alternative to do the same thing is shown below:

func main() {
	i := 0
	for i < 10 {
		fmt.Println("Value of i =", i)
		i++
	}
}

http://play.golang.org/p/6wSSofaQV4

Key thing to note here is that, the for loop only has a condition and does not need preceding and following semicolons. Go is good enough to understand this syntax.

if-else

The behavior of if-else block is the same as in any other language.

func main() {
	p, q := 10, 20
	if p < q {
		fmt.Println("q is greater than p")
	} else if p < q {
		fmt.Println("p is greater than q")
	} else {
		fmt.Println("q and p are equal")
	}
}

http://play.golang.org/p/St4dklsD2l

Output:

q is greater than p

goto

Go supports goto statement. As the name says it all, this statement can be used to transfer control to particular point in the program.

func main() {
	i := 0
	for ; i < 10; i++ {
		if i == 5 {
			goto Print
		} else if i > 5 {
			fmt.Println("Value of i =", i)
		}
	}

Print:
	fmt.Println("Value of i =", i)
}

http://play.golang.org/p/s7vxx3fUfS

Output:

Value of i = 5

In for loop we are checking if value of i is 5, then take the control outside for loop i.e. it will break out of the for loop and will print value of i.

break-continue

The keywords break and continue work in the same was as they do in other languages. The keyword break is for breaking the loop and continue is for skipping the code and moving back to next iteration. In addition to this in Go, break and continue also support a label.

break

func main() {

	flag := true
OuterLoop:
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if i == 2 {
				if flag {
					break
				} else {
					break OuterLoop
				}
			}

			fmt.Println("Value of i:j -->", i, ":", j)
		}
	}

}

http://play.golang.org/p/hCnU9V08O1

Here, we have couple of for loops. The first for loop will loop five times from 0 to 4. In the second for loop we have check if value of i (from outer for loop) is 2. If yes, then it checks for the Boolean variable flag. If flag is true, it will execute break statement and if false will execute break with label OuterLoop. For using break with label, the label needs be set on for, switch or select statements.

Output(flag=true):

Value of i:j –> 0 : 0
Value of i:j –> 0 : 1
Value of i:j –> 0 : 2
Value of i:j –> 0 : 3
Value of i:j –> 0 : 4
Value of i:j –> 1 : 0
Value of i:j –> 1 : 1
Value of i:j –> 1 : 2
Value of i:j –> 1 : 3
Value of i:j –> 1 : 4
Value of i:j –> 3 : 0
Value of i:j –> 3 : 1
Value of i:j –> 3 : 2
Value of i:j –> 3 : 3
Value of i:j –> 3 : 4
Value of i:j –> 4 : 0
Value of i:j –> 4 : 1
Value of i:j –> 4 : 2
Value of i:j –> 4 : 3
Value of i:j –> 4 : 4

In this case the boolean variable flag is true, and hence break will be executed. If you look at the output, you can see iteration for i = 2 is missing. This is because as per the condition, if i == 2 and flag is true, break will be executed and hence, inner for loop will be break, but the outer for loop is still on and hence only iteration for 2 is skipped.

Now if you set flag to false, you will get the following output:

Output(flag=false):

Value of i:j –> 0 : 0
Value of i:j –> 0 : 1
Value of i:j –> 0 : 2
Value of i:j –> 0 : 3
Value of i:j –> 0 : 4
Value of i:j –> 1 : 0
Value of i:j –> 1 : 1
Value of i:j –> 1 : 2
Value of i:j –> 1 : 3
Value of i:j –> 1 : 4

Here, since flag is false, break with label is executed and control moves out of both for loops.

Similarly continue can also have label.

continue

func main() {

	flag := true
OuterLoop:
	for i := 0; i < 5; i++ {
		for j := 0; j < 5; j++ {
			if j == 2 {
				if flag {
					continue
				} else {
					continue OuterLoop
				}
			}

			fmt.Println("Value of i:j -->", i, ":", j)
		}
	}

}

http://play.golang.org/p/G7wtRJl3HK

Output(flag=true):

Value of i:j –> 0 : 0
Value of i:j –> 0 : 1
Value of i:j –> 0 : 3
Value of i:j –> 0 : 4
Value of i:j –> 1 : 0
Value of i:j –> 1 : 1
Value of i:j –> 1 : 3
Value of i:j –> 1 : 4
Value of i:j –> 2 : 0
Value of i:j –> 2 : 1
Value of i:j –> 2 : 3
Value of i:j –> 2 : 4
Value of i:j –> 3 : 0
Value of i:j –> 3 : 1
Value of i:j –> 3 : 3
Value of i:j –> 3 : 4
Value of i:j –> 4 : 0
Value of i:j –> 4 : 1
Value of i:j –> 4 : 3
Value of i:j –> 4 : 4

In this case flag is true and since  j == 2, if you look at the output value if j as 2 is always skipped.

Now if you set flag to false, output will be:

Output(flag=false):

Value of i:j –> 0 : 0
Value of i:j –> 0 : 1
Value of i:j –> 1 : 0
Value of i:j –> 1 : 1
Value of i:j –> 2 : 0
Value of i:j –> 2 : 1
Value of i:j –> 3 : 0
Value of i:j –> 3 : 1
Value of i:j –> 4 : 0
Value of i:j –> 4 : 1

Since we have a condition on j == 2 and flag is set to false, continue with label is executed and when j = 2 in each iteration, control flows back to start of outer for loop and hence only 2 values of j are printed for each value of i.

switch

We all know switch can be used for multi-way expression. Again in Go, the way switch is implemented is slightly different and in my personal opinion the best. Let us look at an example to see how it is done is Go and why I think it is better.

func main() {
	x := 0
	switch x {
	case 0:
		fmt.Println("Value of x =", 0)
	case 1:
		fmt.Println("Value of x =", 1)
	case 2:
		fmt.Println("Value of x =", 2)
	default:
		fmt.Println("Default")

	}
}

http://play.golang.org/p/CVRkNVnWi_

Here, switch will evaluate value of x to find the matching case. We have set x as 0, so case 0 will be matched and executed. And now comes the best part. only case 0 will be executed that’s it. Unlike other languages if you don’t specify a break, the case statements that follow it are also executed.

From your experience with other programming languages and using switch statement in them, you know that 90% of the time, you need to execute only one case. Rarely you need to fall through cases. So you end up explicitly writing break statement for each case, and if you forget to do so chances are some unwanted cases to get executed. But with Go, you don’t need to do so in case, if you need following cases to get executed, you need to mention that explicitly. It is something small but I think a wise and most thoughtful decision.

Output(x=0): Value of x = 0

Output(x=1): Value of x = 1

Output(x>2): Default

Example of fallthrough:

func main() {

	x := 1
	switch x {
	case 0:
		fmt.Println("Value of x =", 0)
	case 1:
		fmt.Println("Value of x =", 1)
		fallthrough
	case 2:
		fmt.Println("Value of x =", 2)
	default:
		fmt.Println("Default")

	}

}

http://play.golang.org/p/hwd7nQS4vD

Here we have added fallthrough at the end of case 1, so now when value of x = 2, we will get following output

Value of x = 1
Value of x = 2

Point to note here is that fallthrough made case 2 to execute irrespective of whether case matches. But since case 2 does not have fallthrough the implicit break, breaks the flow and default is not executed.

defer

This keyword is used to defer execution at the end of function. This statement can appear anywhere in the function, but will be executed at the end of the function. Let us look at the example below to understand more.

func main() {

	defer completed()
	fmt.Println("Start execution")
	validate()
}

func completed() {
	fmt.Println("Successfully completed execution")
}

func validate() {
	fmt.Println("Validation successful")
}

http://play.golang.org/p/DVh9Xqg0sb

In above example, we have 2 functions completed and validate. On first line of main function we call defer to completed function. This will defer the call to the function completed until the end of the main function.

Output:

Start execution
Validation successful
Successfully completed execution

Conclusion

We got introduced to various control flow statements like for, if-else, switch etc. Go does not have a lot of control statements, but they have made changes to use the existing ones to handle most of the use cases. In the next post, we will look at more types like struct, array, slices etc.

comments powered by Disqus