Welcome To Golang By Example

Understand for-range Loop in go (golang) – Complete Guide

This is the  chapter 11 of the golang comprehensive tutorial series. Refer to this link for other chapters of the series – Golang Comprehensive Tutorial Series

Next Tutorial – If Else
Previous Tutorial – For Loop

Now let’s check out the current tutorial. Below is the table of contents for current tutorial.

Overview

When it comes to loop, golang has:

We saw for loop in the last tutorial. In this tutorial, we will be learning about the for-range loop only.

for-range loop is used to iterate over different collection data structures in golang such as

Let’s see some examples now

Examples

for-range loop for array/slice

Here is the format of for-range when used with array/slice

for index, value := range array/slice {
    //Do something with index and value
}

This is how for-range loop works in case of array/slice. It iterates over the given array/slice starting from index zero and the body of the for range loop is executed for every value present at the index. Both index and value are optional in for-range when using with array/slice.

The below example shows how to use a for-range loop for a slice

package main

import "fmt"

func main() {
    letters := []string{"a", "b", "c"}

    //With index and value
    fmt.Println("Both Index and Value")
    for i, letter := range letters {
        fmt.Printf("Index: %d Value:%s\n", i, letter)
    }

    //Only value
    fmt.Println("\nOnly value")
    for _, letter := range letters {
        fmt.Printf("Value: %s\n", letter)
    }

    //Only index
    fmt.Println("\nOnly Index")
    for i := range letters {
        fmt.Printf("Index: %d\n", i)
    }

    //Without index and value. Just print array values
    fmt.Println("\nWithout Index and Value")
    i := 0
    for range letters {
        fmt.Printf("Index: %d Value: %s\n", i, letters[i])
        i++
    }
}

Output:

Both Index and Value
Index: 0 Value:a
Index: 1 Value:b
Index: 2 Value:c

Only value
Value: a
Value: b
Value: c

Only Index
Index: 0
Index: 1
Index: 2

Without Index and Value
Index: 0 Value: a
Index: 1 Value: b
Index: 2 Value: c

for-range loop with a string

In Golang string is a sequence of bytes. A string literal actually represents a UTF-8 sequence of bytes. In UTF-8, ASCII characters are single-byte corresponding to the first 128 Unicode characters. All other characters are between 1 -4 bytes. To understand it more consider the below string

sample := "a£c"

In above string

The above string has 1+2+1 = 4 bytes altogether. Therefore when we try to print the length of the string using the standard len() function it will output 4 and not 3 as len() function returns the number of bytes in the string.

fmt.Printf("Length is %d\n", len(sample))

Hence standalone for loop cannot be used to iterate over all characters of a string as it will iterate over bytes and not character. So below for loop will instead iterate four times and the print value corresponding to the byte present at that index.

 for i := 0; i < len(sample); i++ {
    fmt.Printf("%c\n", sample[i])
 }

It will output below string which is not same as "a£c" string

a£b

The above output is not what we want. This is where for-range loop comes into picture for a string. It iterates over the Unicode points( also referred to as rune in golang) in a string and will correctly output  a, £, b.  Here is the format when using for-range with string

for index, character := range string {
    //Do something with index and character
}

Some point to note before we move to a code example

Now let's see a code example

package main

import "fmt"

func main() {
    sample := "a£b"

    //With index and value
    fmt.Println("Both Index and Value")
    for i, letter := range sample {
        fmt.Printf("Start Index: %d Value:%s\n", i, string(letter))
    }

    //Only value
    fmt.Println("\nOnly value")
    for _, letter := range sample {
        fmt.Printf("Value:%s\n", string(letter))
    }

    //Only index
    fmt.Println("\nOnly Index")
    for i := range sample {
        fmt.Printf("Start Index: %d\n", i)
    }
}

Output:

Both Index and Value
Start Index: 0 Value:a
Start Index: 1 Value:£
Start Index: 3 Value:b

Only value
Value:a
Value:£
Value:b

Only Index
Start Index: 0
Start Index: 1
Start Index: 3

for-range loop with a map

In case of map for-range iterates over key and values of a map. Below is the format for for-range when using with a map

for key, value := range map {
    //Do something with key and value
}

A point to be noted that both key and value are optional to be used while using for-range with maps. Let's see a simple code example

package main

import "fmt"

func main() {
    sample := map[string]string{
        "a": "x",
        "b": "y",
    }

    //Iterating over all keys and values
    fmt.Println("Both Key and Value")
    for k, v := range sample {
        fmt.Printf("key :%s value: %s\n", k, v)
    }

    //Iterating over only keys
    fmt.Println("\nOnly keys")
    for k := range sample {
        fmt.Printf("key :%s\n", k)
    }

    //Iterating over only values
    fmt.Println("\nOnly values")
    for _, v := range sample {
        fmt.Printf("value :%s\n", v)
    }
}

Output

Both Key and Value
key :a value: x
key :b value: y

Only keys
key :a
key :b

Only values
value :x
value :y

for-range loop with a channel

for-range loop works differently too for a channel. For a channel, an index doesn't make any sense as the channel is similar to a pipeline where values enter from one and exit from the other end.

So in case of channel, the for-range loop will iterate over values currently present in the channel. After it has iterated over all the values currently present (if any), the for-range loop will not exit but instead wait for next value that might be pushed to the channel and it will exit only when the channel is closed

Below is the format when using for-range with channel

for value := range channel {
    //Do something value
}

Let's see a code example

package main

import "fmt"

func main() {
    ch := make(chan string)
    go pushToChannel(ch)
    for val := range ch {
        fmt.Println(val)
    }
}
func pushToChannel(ch chan<- string) {
    ch <- "a"
    ch <- "b"
    ch <- "c"
    close(ch)
}

Output:

a
b
c

Conclusion

This is all about for-range loop in golang. Hope you like it. Please share feedback/improvements/mistakes in comments.

Next Tutorial – If Else
Previous Tutorial – For Loop