What the for? One loop to rule them all!
Tags: go, beginners, codenewbie
Published: 2019-12-31 12:37:35

When I first learned that Go has only one keyword for loops, I thought to myself "What the f***? Aren't they oversimplifying things?".

Now, looking back, I think getting rid of `while` and `do...while` in favor of having only `for`, is a good choice.

I wouldn't say this is easier to learn for beginners. Replacing several keywords by several forms of the same keyword doesn't minimize what you have to learn as a total newbie.

However, having used Go for several years now, I feel comfortable with its `for` loop keyword. Writing it or reading it from other people's code always feels simple.

Now let's have a look at its different syntaxes.

## Forever

Starting with my favorite one:

```go
package main

import "fmt"

func main() {
	// Don't try this at home
	for {
		fmt.Println("To infinity and beyond!")
	}
	// This infinite loop prints a message to standard output
}
```

[Run it on Go playground](https://play.golang.org/p/3Jm4mtLzoAR)

Nice and pure, the infinite loop needs only the `for` keyword and its brackets.

This is compact, and in my opinion pretty intuitive.

## Good ol' for

Of course there is the classic `for`, with an initialization, a condition, and an afterthought, separated by semicolons:

```go
package main

import "fmt"

func main() {
	// Count to ten on standard output:
	for i := 1; i <= 10; i++ {
		fmt.Println(i)
	}
}
```

[Run it on Go playground](https://play.golang.org/p/WVdG-BXOMpp)

Notice the absence of parentheses? Go doesn't have parentheses around statement's parameters.

Each part may be ommited:

```go
package main

import "fmt"

// This function counts down from n to 1
func countDown(n int) {
	// No initialization is needed
	for ; n >= 1; n-- {
		fmt.Println(n)
	}
}

func main() {
	countDown(10)
}
```

[Run it on Go playground](https://play.golang.org/p/4lIi7rNrIIW)

The two semicolons must be kept for the syntax to be valid (only one semicolon is forbidden).

You may omit all the parts, which is the equivalent of an infinite `for` loop.

## For a while

If you need the equivalent of a `while`, that is a loop with only a condition, just use `for` like you would use a `while`:

```go
package main

import "fmt"

// This function adds leading zeros to str
// in order ot reach a length of n
func padZeros(str string, n int) string {
	for len(str) < n {
		str = "0" + str
	}
	return str
}

func main() {
	// Prints "0000000123"
	fmt.Println(padZeros("123", 10))
}
```

[Run it on Go playground](https://play.golang.org/p/uITCWmYNK3F)

Not much to say, this is rather intuitive, still no parentheses.

## `for...range`

A common use case is to iterate over a list of items.

This can be done using the `range` keyword in conjunction with `for`:

```go
package main

import "fmt"

func main() {
	var messages = []string{"May", "the", "for", "be", "with", "you"}

	// Prints each message preceded by its index
	for i, message := range messages {
		fmt.Println(i, message)
	}
}
```

[Run it on Go playground](https://play.golang.org/p/8fNEO7k2qZd)

As you can see, `for...range` allows to retrieve each index and its associated value.

`range` may also be used with maps, in which case it retrieves a key instead of an index.

If you need only the value, the index/key may be discarded using `_`:

```go
package main

import "fmt"

func main() {
	var messages = []string{"May", "the", "for", "be", "with", "you"}

	// Prints each message
	for _, message := range messages {
		fmt.Println(message)
	}
}
```

[Run it on Go playground](https://play.golang.org/p/Eeo_X1IquNc)

And in case you need only the index/key, the value may be ommited:

```go
package main

import "fmt"

func main() {
	var messages = []string{"May", "the", "for", "be", "with", "you"}

	// Prints each message
	for i := range messages {
		fmt.Println(messages[i])
	}
}
```

[Run it on Go playground](https://play.golang.org/p/NAjoynK1EcO)

Finally, if you don't need the index/key or the value, both can be omitted:

```go
package main

import "fmt"

func main() {
	var messages = []string{"May", "the", "for", "be", "with", "you"}

	// Allows you to iterate len(messages) times
	for range messages {
		fmt.Println("Do something...")
	}
}
```

[Run it on Go playground](https://play.golang.org/p/2uUmnHXFTZq)

## `for...range` with channels

Go is well known for its <abbr title="Communicating Sequential Processes">CSP</abbr> concurrency model, which favors goroutines and channels over threads and mutexes.

`for...range` may also be used when receiving values from a channel:

```go
package main

import "fmt"

func main() {
	ch := make(chan int) // Create a channel
	go send123(ch)       // Start send123() in a new goroutine

	// Receive and print integers from channel
	for i := range ch {
		fmt.Println(i)
	}
}

func send123(ch chan int) {
	// Send 3 integers through channel
	for i := 1; i <= 3; i++ {
		ch <- i
	}

	close(ch) // Close channel
}
```

[Run it on Go playground](https://play.golang.org/p/wjlAnWLY8J4)

Used on a channel, `for...range` will wait for a value to be received at each iteration, blocking the goroutine which is executing it.

The loop ends when the channel is closed by the sending goroutine.

## Conclusion

As you can see having only one keyword for loops doesn't change things much if you were used to C-style languages.

It makes for rather understandable code, which I think is one of the most important things if you have to work with a team.

I hope you enjoyed this quick introduction to Go's loops, give a ❤️, 💬 leave a comment, or share it with others, and follow me to get notified of my next posts.