Sunday, January 10, 2021

Go (golang) Loops - A Unified "for"

My primary world has been C# for a while. I've been looking at the Go programming language (golang), and I wanted to write a few articles about things I find interesting about the language. Here is the first:
The "for" statement is used for all loops in Go.
In C#, we have quite a few ways to loop, including "for", "foreach", "while", and "do while". Go uses a single "for" statement to handle all of these situations.

Motivation: I have been using C# as a primary language for 15 years. Exploring other languages gives us insight into different ways of handling programming tasks. Even though Go (golang) is not my production language, I've found several features and conventions to be interesting. By looking at different paradigms, we can grow as programmers and become more effective in our primary language.

Resources: For links to a video walkthrough, CodeTour, and additional articles, see A Tour of Go for the C# Developer.

Looping with an Indexer

In C#, the "for" loop sets up an indexer. The Go "for" statement can be used this same way.

C#
    // Print the numbers 0 through 9
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(i);
    }

Go
    // Print the numbers 0 through 9
    for i := 0; i < 10; i++ {
        fmt.PrintLn(i)
    }

The syntaxes are quite similar. Some differences: 
  • Go does not have parentheses around the condition.
  • Go has the opening brace on the same line as the "for".
  • Go requires the braces even if the body only has 1 line of code.

Iteration

In C#, the "foreach" loop is handy to iterate over a collection (in reality, anything that implements the IEnumerable interface). Go uses a "for" statement with a range to do this same thing.

C#
    // "weekdays" is an array with the days of the week
    foreach(var day in weekdays)
    {
        Console.WriteLine(day);
    }

Go
    // "weekdays is an array with the days of the week
    for index, day := range weekdays {
        fmt.Println(weekdays[index])
        fmt.Println(day)
    }

A big difference in Go is that "range" returns 2 values: the index and the item. If we use the index, then we can index into the collection to get a value out. But if that's what we're doing, we can instead use the value directly.

Side note about unused variables
In Go, if a variable is declared but not used, the result is a compiler error. With a "range", it is common to use the indexer *or* the item, but it is uncommon to use both. In that situation, we can use a "blank identifier" (the '_' character) to note that we are not going to use that value. This is similar to using a "discard" (also the '_' character) in C#.

Here is a "for" loop that is more equivalent to a "foreach" in C#:

Go
    // "weekdays" is an array with the days of the week
    for _, day := range weekdays {
        fmt.Println(day)
    }

Simple Iteration
One other option with a "range" is to capture neither the indexer nor the item. This will run the body of the loop one time for each item in the range, but it does not use the index or the item in the body.

Go
    // Run a loop one time based on the length of the range
    for range weekdays {
        // do something here
    }

This is limited because we do not have access to the thing that we are iterating, but it does let us perform an action a certain number of times (based on the length of the collection).

While Loops

The "for" statement in Go can also be used as a "while" loop.

C#
    while (command != "stop")
    {
        // do something with the command
        command = GetNextCommand();
    }

Go
    for command != "stop" {
        // do something with the command
        command = getNextCommand()
    }

The statements are similar between C# and Go. The condition is set with the "while" or "for", and the state that affects the condition is updated inside the loop.

do / while
There isn't really a direct way to do a "do / while" loop in Go. In C#, the body of a "do / while" loop will execute at least one time, and the condition is checked after the first iteration of the loop. Personally, I haven't seen a "do / while" loop in quite a while in C#. Generally a "do / while" can be refactored into a standard "while", so this is not a large issue.

Endless Loops

Sometimes we need an endless loop -- okay, generally not endless, but a loop that runs until an explicit "break".

C#
    while (true)
    {
        // do something interesting
        if (stopCondition)
            break;
    }

Go
    for {
        // do something interesting
        if stopCondition {
            break
        }
    }

The "for" statement with no condition sets up an endless loop.

Note: the above format is one way to refactor a "do / while" loop. The "do something interesting" section will run at least one time.

"break" and "continue"

The "break" and "continue" statements in Go work the same as in C#. The "continue" statement will stop the current iteration of the loop and go to the next iteration. The "break" statement will break out of the loop entirely.

Final Thoughts

There are a lot of interesting things about the Go language and the design decisions that were made. I think it's very interesting that the "for" statement can cover so many use cases -- things that require "for", "foreach", and "while" in C#. There are pros and cons to this approach.

One "pro" is that there's only one statement to worry about -- if you need a loop, you use "for".

The "con" that goes along with this is that there are many different things that can go in the condition, whether it is an indexer, a range, or an empty condition.

Personal preference, I kind of like the single "for" statement.

Even if you don't end up using Go as a primary language (or even at all), it is interesting to look at how other languages approach certain problems. It can give us ideas of how we can approach things differently in our primary programming language.

Happy Coding!

1 comment:

  1. Nice work. I wrote an article about Go's multifaceted for loop myself: https://golangprojectstructure.com/various-types-of-for-loop-in-go/

    ReplyDelete