Golang Project Structure

Tutorials, tips and tricks for writing and structuring code in Go (with additional content for other programming languages)

Why Does Go Have So Many Different Kinds of For Loop?

Language

  • unknown

by

Anyone with experience of a C-based programming language will be immediately aware of how useful and fundamental for loops are.

C also has while and dowhile loops. JavaScript has a forEach method, which allows us to iterate over arrays, and it also has iterator objects.

Go only really has the for loop. However, this single type of loop does have many variants, which is why it can sometimes seem trickier to understand for people who are new to the Go programming language — but mastering the variants is really simple.

I’ll prove it to you. In this blog post, we’re going to discuss the different variants of Go’s for loop and where they come in handy.

By the end, you should see what I mean about loops in Go being really easy to grasp.

The Infinite Loop

The simplest type of for loop in Go just uses the keyword followed by a pair of braces.

Whatever code is contained between the opening and closing braces will run forever — or, at least, until the program or loop is stopped in some other way.

For example, in the code below, a message will be printed out every second and it will keep being printed so long as the program is running:

package main

import (
	"fmt"
	"time"
)

func main() {
	for {
		time.Sleep(time.Second)
		fmt.Println("A SECOND HAS PASSED")
	}
}

We don’t need to include any condition after the for keyword, because it’s simply assumed that, without specifying a condition, the code between the braces will run for an indefinite number of iterations.

In C, however, we would have to include at least an empty conditional (by using two consecutive semicolons) in our for loop, like so:

#include <stdio.h>
#include <unistd.h>

int main() {
    for (;;) {
        sleep(1);
        printf("A SECOND HAS PASSED\n");
    }
    
    return 0;
}

You can also, of course, write an infinite loop like this in C: while (true) { /* more code here */ }.

Go’s syntax is much simpler: since we know that an infinite loop will always run for an indefinite amount of iterations, there’s no need to supply any kind of condition.

Replacing the C-Style While Loop

Go doesn’t have a while keyword, but its for loop can easily mimic the functionality of a traditional C-style while loop.

Instead of using while, we simply write a for loop with a single condition, as shown in the example below:

package main

import "fmt"

func main() {
	fmt.Println("Commencing the countdown:")

	count := 10

	for count > 0 {
		fmt.Printf("\t%d\n", count)

		count--
	}
}

Note also that we don’t write the condition between brackets, as we would do in C.

Compare this to similar C code that utilizes a traditional while loop:

#include <stdio.h>

int main() {
    int n = 10;
    
    while (n) {
        printf("%d\n", n--);
    }
    
    return n;
}

As we stated earlier, Go replaces the while loop by simply using the for loop with a condition.

Once the condition becomes false, the loop terminates, just as we would expect in C.

You can also see that we have incremented the n variable within the print statement here, which wouldn’t have been possible in Go, where an increment operation always stands on its own and cannot be combined with other operations.

This is a good example of the difference between the fundamental philosophies of the two languages — Go prizes elegant simplicity, whereas C makes it easier for a programmer to show off by using terse and clever statements that are idiomatic to the language.

Initializing a Loop-Scope Variable

In Go, you can initialize a variable directly within the loop statement itself. This is typically done in the first part of the for loop header. A loop-scope variable can be both declared and initialized.

This variable is scoped exclusively to the for loop and is accessible only within the loop body. Once the loop completes its execution, the variable is discarded.

This feature provides a particularly useful way to keep your code clean and concise, since it allows you to manage the iteration variable within the loop itself, avoiding the need for writing separate variable declarations outside the loop.

Here’s an example of a for loop with a variable initialized in the loop header:

package main

import "fmt"

func main() {
	for i := 0; i < 5 {
		fmt.Printf("Iteration: %d\n", i)
                i++
	}
}

By initializing the i variable inside the loop header, we keep the scope of the variable limited to the loop itself, which can help prevent accidental misuse elsewhere in the program.

Note that this can also be done in C, but there must always be a second semicolon included within the loop header:

#include <stdio.h>

int main() {
    for (int i = 0; i < 5;) {
        printf("Iteration: %d\n", i);
        i++;
    }
    
    return 0;
}

This C code features a for loop that initializes a variable and checks a condition, but it does not include an increment statement within the loop header. Instead, the increment occurs within the loop itself.

The Classic For Loop

This is your grandfather's for loop — the traditional loop with an initialization, a condition and a post-loop action all contained within the one-line header.

It’s the good old style that will be familiar to developers who have any experience in languages like C, C++ or Java.

Let's look at an example in Go:

package main

import "fmt"

func main() {
	var sum int

	for i := 1; i <= 10; i++ {
		sum += i
	}

	fmt.Println(sum)
}

The example code above adds up all the numbers from 1 to 10 inclusive and then prints out the result.

The for loop with a three-part header that we use here initializes i to 1, runs while i is less than or equal to 10 and increments i after each iteration of the loop.

It’s a direct match to what you’d expect from a classic for loop in C, as shown in the corresponding code below:

#include <stdio.h>

int main() {
    int sum = 0;

    for (int i = 0; i < 10; i++) {
        sum += i;
    }

    printf("%d\n", sum);
    
    return 0;
}

The two code examples are very similar, except that C uses brackets around the loop header, whereas Go does not.

Note also that the sum variable is implicitly initialized to zero in Go, whereas it has to be explicitly initialized in C.

Comparing the Mathematical Summation Operator

If you have any familiarity with mathematical notation, you might have noticed that the for loops that we used in the code examples in the previous section were used in a way that's like that of the summation operator (Σ) commonly used in mathematics.

In mathematics, the summation operator is often written as shown below:

The mathematical notation used for the summation operator.

This represents the sum of each integer i between 1 and n.

If we set n to 10, then we are performing the same work using the summation operator that we did in the previous examples with the use of a for loop.

In other words, we are adding up all the numbers between 1 and 10 inclusive and returning the result.

Now, after that brief mathematical interlude, let's move on to look at our final form of the for loop in Go...

The For Loop With a Range

One of the most powerful features of Go’s for loop is the ability to add a range clause, which allows you to iterate over data structures like arrays, slices, maps and strings.

This simplifies working with collections of data and eliminates the need to manually track the length of the structure.

Let's look at an example that iterates over a slice:

package main

import "fmt"

func main() {
	numbers := []int{1, 2, 3, 4, 5}

	for idx, num := range numbers {
		fmt.Printf("Index: %d, Value: %d\n", idx, num)
	}
}

This is much simpler than manually handling indices and using each index to access each element individually.

In C, you would have to do something like the following:

#include <stdio.h>

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int length = sizeof(numbers) / sizeof(numbers[0]);

    for (int i = 0; i < length; i++) {
        int num = numbers[i];

        printf("Index: %d, Value: %d\n", i, num);
    }

    return 0;
}

The version written in C is clearly more verbose.

With Go’s for-range loop, both the index and the value are provided automatically.

However, if you prefer, you can access just the index (and not the value) in Go if you only declare one variable in the header of a for loop that has a range clause.

We also did not have to calculate the length of the slice in Go, or provide it in the loop header, since the for-range loop will always stop iterating when all of the elements in the collection have been traversed.

Why Does Go Have So Many Types of For Loop?

At first glance, it may seem like Go’s for loop is trying to be too versatile by replacing do, while, foreach and other such constructs from related programming languages.

But this simplification is intentional, because whenever we see the for keyword in Go, we know that we’re dealing with a loop — whether it's infinite, conditional or based on a collection.

There's no way to create a loop in Go other than by using the for keyword, except by using the goto keyword, which is generally discouraged by many programmers, since it can lead to spaghetti code that is unnecessarily convoluted.

A long-exposure photograph of a roundabout at night. No cars can be seen, but the trail left by their white and red headlights and taillights remains.
Loops allow us to revisit code, just as roundabouts allow traffic to revisit roads.

By keeping loops very consistent, Go helps to avoid the complexity that comes from having too many ways to do the same thing.

It’s a language designed for simplicity, and this philosophy extends to its loop structure as well.

Leave a Reply

Your email address will not be published. Required fields are marked *