HomeDevelopmentDevOpsIt is time to Go: our journey into grown-up code part 4

It is time to Go: our journey into grown-up code part 4

This is the fourth article in our series on learning GO.  The first three articles can be found here:

  1. It is time to “Go” and learn how to code with Go
  2. It is time to Go: our journey into grown-up code part 2
  3. It is time to Go: our journey into grown-up code part 3

To date, we have discussed several core features of the language, continuing to build your foundation of knowledge:

  • Variables types
  • Variadic variables
  • Redeclaration
  • Constants
  • inputs
  • Structs
  • Pointers
  • Interfaces
  • Function basics
  • Conditional statements, if statements and switch statements.

At the end of the last article, we discussed the conditional statement, these the if/else, else if and switch constructs are a core building block for logic control in Go; today, we will look at how Go handles loops.

Time to go around the loop

The first question to ask is, what is a loop? At the base level, it is block of code that is run repeatedly through a set of instructions.  Logically this makes no sense, why would you have written a piece of code that would just go around in circles, I don’t know, but they do, and they even have a name for it “the infinite loop”.

One thing I have found while studying is that unlike in C, C++ and C# where there are three types of loops The for, while and the do/while; apparently, Go only has a single loop, the for loop.  This is significant as it means there is less to understand, and it potentially simplifies the language. OK so that is interesting – get on with the knowledge transfer Tom.

What is a loop made up of?

A loop must have four constituent parts to make it valid, three are concerned with the instantiation of the loop and the final is the payload; there is the initial statement or the “init” clause.  The second is the “condition” statement, which is how long this will be done for; or the loop will continue for X number of iterations. The third part is the “post statement”, this is effectively your incrementor statement.  The forth part is the action, or payload; it is what you are actually doing.

for [ Initial Statement ] ; [ Condition ] ; [ Post Statement ] {
[Action]
}

Whilst looking to understand the process of the loop I came up with this flow diagram, it aided me, hopefully it will aid you the reader.

What is a loop in Go
What is a Loop in Go

So now that we know the format of a loop, lets have a look at what we can do. Again, like we did with the conditional statements snippets we are not going to actually do anything useful with the code we generate; we are going to run through some of the things we can do.

Loops in Go – The for Loop

The simplest loop in Go is the “for” loop, remember what we stated about the construct of a loop in Go.

// Go program to illustrate the use of simple for loop
package main
import . "fmt"
// Main function
func main() {
   // for loop
   // This loop starts when i = 0
   // executes till i<4 condition is true
   // post statement is i++
   for i := 0; i < 4; i++{
      Printf("My Name is “Slim Shady”\n")
   }
}

The astute amongst you may have noticed a problem in this code.  Running this as shown above will result in an error as shown below:

GoError
Ohh, That does not look nice, what went wrong there.

Why? simple we have double quotes in the string to be printed out.  Go will interpret this as “My Name is” do something else, “\n”, this is obviously not our intention, what we want is print to standard out “My Name is “Slim Shady” and insert a new line.

To obtain the desired result of having Slim Shady in quotes we will add what is called an escape character a simple “\”. You have seen a use of this character before with the new line character “\n” This way the line would read:

Printf("My Name is \“Slim Shady\”\n")

this would result in the expected response as show below:

My Name is Slim Shady
My name is, my name is “Slim Shady”

So let’s have a look what we have done.  Firstly we initialised our loop with our first position “i := 0”, an integer with a value of 0,  next we stated how long the loop was run, or rather how many iterations we will loop through, “i < 4”, the loop will run as long as “I” is less than 4, finally we stated what our increment size will be with the statement “i++” in long form this would better read “i = i + 1”.  So, is our result as expected?  Yes, it is as we have four iterations of the string printed to standard output.

How do we know this?  Lets have a look at another example of a For Loop. Using integers rather than a string.

package main
import . "fmt"
func main(){
   s := []int{0,2,4,6,8}
   for i := 0; i < len(s); i++{
      Printf("%v: %v\n", i, s[i])
   }
}

Here we have defined a variable called “s” as an integer and assigned it a value, this value is a list of five numbers.

One thing to note is that we have also introduced another new concept the “slice”.  What! Where? If you re-read the instantiation of the “s” variable you will note that there are two square brackets preceding the “int” statement. This defines a “slice”. We will discuss slices in more depth later but for now. Briefly as slice is like an array (a set of values).

Running the code above will result in the response shown below:

length and values in Go
So how many times and what our values are.

Here we can clearly see the iterations of the loop (0 through to 4) as the code runs and the members of the slice (0, 2, 4 ,6, 8).  You will also note that there is an new command len(s), this means that the loop will reduce by one for the size of the slice, in our case we have 5 entities in the slice therefore we will have 5 iterations.

Now for a little short cut, with Go you can assign multiple entities to the initialisation statement.  Rewrite the code to read as below and run it again.

package main
import . "fmt"
func main(){
   for i, s := 0, []int{0,2,4,6,8};i < len(s); i++{
      Printf("%v: %v\n", i, s[i])
   }
}

Next we will consider variable shadowing, here we can define two slice variables “s” as we have two “init” variables “i”

func main(){
    s := []int{9, 7,  5, 3, 1}
    for i, s := 0, []int{0, 2, 4, 6, 8}; i < len(s); i++ {
       Printf(“%v: %v\n”,I s[i])
   }
   for i := 0; i < len(s); i++ {
      Printf(“%v: %v\n”, i, s[i])
   }
}

Because the have redeclared the “s” variable in the first for block, we can still use the first “s” declaration for the second for block.

Running the full code for this function will result in the following:

oh Look two slices
Now we are being greedy, two slices in one function.

Now lets consider the concept of an infinite loop,  this is where a loop has no pre-defined exit point.  Why would you want something like the?  To consider this concept, lets step outside of go and back into the real world and consider where this concept is seen.  Linux users often use the tail -f command to list the tail of a command and see it add entries to the end of the file in real time.  This command will continue until such a time as the user interacts with the terminal interface with an exit response like pressing ctrl-x.

The infinite loop is a similar concept.  It is created by leaving out the iteration statement.

func main(){
   s := []int{0, 2, 4, 6, 8}
   for i := 0; ; i++ {
      printf(“%v: %v\n”, I, s[i])
   }
}

Update your function to read as above and rerun the code.

Yugh, that is not pretty
Yugh! That is not pretty, another panic – that is just not pretty.

This is a called panic and it is not a good thing, it caused because we have a finite slice of 5 entities, that is attempting to loop a sixth time.  We cannot have our programs crashing out as effectively as this every time, so there must be something we can do to fix this?  Yes there is welcome to the concept of a deferred function and the recover() task.

package main
import . "fmt"
func main() {
   defer func() {
      recover()
   }()
   s := []int{0, 2, 4, 6, 8}
   var i int
   for {
      Printf("%v: %v\n", i, s[i])
   i++
   }
}

Running the code will result in the well know response:

length and values in Go
See, once again, different code and the same result

This looks a nice way to escape an infinite loop, but it is generally considered a very bad idea to use the “recover()” process in this way; this is because when a Go program panics, because it means that something has go seriously wrong. It seems that as nice as “recover()” looks it is the “go” equivalent of “sawing a branch of a tree whilst sat on the branch”

Not the result you expect
Not the Result you need. (copyright “https://de.toonpool.com)

So as “recover()” and “defer func()” is not a good why of handling infinite loops as it just kills the program for something that could be fairly innocuous.  How should we handle this? My current answer is I do not know.  Like you, I am still learning, but we will find out 😊.

Loops in Go – The while Loop

We will now move on to the second type of loop, the While loop.  This type of loop will continue to run while a condition is true, once the condition is false the loop ends.   Lets look at a very simple while loop and once again break it down line by line.

package main
import . "fmt"
func main() {
   i:= 0
   for i < 3 {
      i += 2
   }
   Println(i)
}

The first thing you should notice is that nowhere in this loop is the word “while” mentioned.  This is because there is no “while” key in GoLang.  OK, so how can this be a “While” loop? The simple answer is because the block mimics the concept of the “while loop” in C and C++. The “While” loop has a completely different method of instantiation to the traditional “for” loop we discussed earlier; but it is still a “for” loop.  This is because the instantiation and the post statements are optional in GoLang.  So looking at this code block we can see that the initialisation statements and the post statements are removed from the instantiation statement; all that we are left with prior to the curly brackets is the condition statement. The initialisation statement is now just a variable “i := 0” and it appears that the post statement is now the object of the loop.  So what exactly is happening here?

In this simple program, our “for” loop, sorry our “while” loop executes until the condition “i < 3” (i is less than 3) is true. The variable “i” is initially declared with the value of “0”, and the loop body increments “i” by “2” in each iteration, this statement is also new, if we remember our original for loop, we the post statement iterated by a value of 1 as shown by the statement “i++”, here we have a statement that reads “i += 2“, this is the method that we use to increment a number by a greater or lessor number than one.  For example, “i += 0”, although why you would want to do this is very questionable as you are never incrementing the loop. In our current case, we are incremented the integer “i” by “2” each loop cycle, once the loop has run enough times to turn the statement false, or that “i” is now greater than the value 3, the loop breaks and we fall through to the action of the loop, in our case print to standard output the value of 4 when the loop broke.

So why would we use this and what is the fundamental difference?

OK, so why do we need a while statement? In C or C++ the use case of a While block is to have code loop, until a statement is false. Or to be more precise, have code run thought an unknown number of statements, until the value you need is present, then continue on with the process.  A good example in a real environment could be a password capture box, where the number of attempts are unknown, that said, perhaps that is not such a good example LOL; do we really want our users to be able to continually guess a password with out the ability to drop into an account lock.

OK let’s Go and Do something While the sun shines

OK the next loop we will look at is the do-while loop, this is a conditional statement block that will executes a block of code at least one.  This code will repeatedly run until the conditional statement is met.

Once again this is not a traditional Do-While loop as Go contains does not contain either a “while” expression or a “do” expression.  So once again it is a variable of the “for” loop.

In go there are two methods of constructing a Do-While loop.

package main
import . “fmt”
func main (){
   i := 0
   for next := true; next; next = i < 5 {
      Println(i)
      i++
   }
}

As per usual, what is happening above?  From now on we will only discuss the doing parts of function or loop,  by now we should know what these mean.

for next := true; next; next = i < 5 {
   Println(i)
   i++

what does our “for” statement say?  The first statement after the for word initialises and assigns the value “true” to the variable “next”,  the second statement is the condition that is validated each iteration, in this case it is the value “true”. Finally the third statement “next =  i < 5”, which means that the loop will continue until the next equals “5”.  Whilst “next” is below “5” the action will print to standard output the current value of next.  Finally, each iteration will increment the value of “i” by one, prove this by running this code which results in the following response:

Our first Do While loop.
it is not a real While loop but it works in exactly the same manner.

The second method of emulating a do-while loop in go is shown with the following code

i := 10
for {
   Println(i)
   i++
   if i >= 5 {
      break
   }
}

This time the code initialises the variable “I” to “10”. The for loop then executes the block of code inside it repeatedly until the break statement is executed. In each iteration of the loop, the value of i is printed to the console using the “Println(i)” statement, and then the value of “i” is incremented by 1 using the “i++” statement.

The if statement checks whether the value of “i” is greater than or equal to “5”. If the condition is true, the break statement is executed, which causes the loop to exit. As the initial value of “i” is “10” the loop only runs once.

So, the output of this code when run is:

do-while two ways.
this time we only run once, and 10 was the variable and that is above 5

The final piece to note is the introduction of the “break” word.  This is a method of escaping a loop without or a switch statement without “sawing of the branch whilst sitting on the branch”.  When the word is encountered in a loop that loop is immediately terminated and the program resumes at from the next statement in the loop.  It can also be used to terminate a case statement in a switch block.

Summary

Once again, we have covered a lot of ground, this has been a bit of a slog, but understanding loops is imperative as it is the core building block of all programming languages.  We have only scraped the surface of loops, and if you read this article it will seem that loops can only deal with integers.

This seems to be quite restrictive.  So in our fifth article we will put on the deep dive gear and investigate some of the more arcane parts of loops thank you for reading and continuing on the journey into Go.

NEWSLETTER

Receive our top stories directly in your inbox!

Sign up for our Newsletters

spot_img
spot_img

LET'S CONNECT