How to Iterate Over a Slice

To iterate over a slice in Go, create a for loop and use the range keyword:

package main

import (
	"fmt"
)

func main() {
	slice := []string{"this", "is", "a", "slice", "of", "strings"}

	for index, itemCopy := range slice {
		fmt.Printf("%v: %v\n", index, itemCopy)
	}
}

(Run in Playground)

The output will be:

0: this
1: is
2: a
3: slice
4: of
5: strings

As you can see, using range actually returns two values when used on a slice. The first is the index of the value in the slice, the second is a copy of the object. Note that it is not a reference to the actual object. This means if you modify the copy, the object in the slice won’t necessarily change.

As an example, don’t do this:

package main

import (
	"fmt"
)

func main() {
	slice := []string{"this", "is", "a", "slice", "of", "strings"}

	// Exclamation marks will only be appended to the copy.
	for _, itemCopy := range slice {
		itemCopy = itemCopy + "!"
	}

	// They won't be present in the output.
	for index, itemCopy := range slice {
		fmt.Printf("%v: %v\n", index, itemCopy)
	}
}

(Run in Playground)

Output:

0: this
1: is
2: a
3: slice
4: of
5: strings

Two things to note, first is you can use an underscore _ to discard a range value if it is not used in the loop. Second, and more importantly, is to emphasise that the copy really is a copy and won’t alter the actual object. A slice of references will behave the same way, the key difference, however, is that a copy of a reference will still point to the same object – dereferencing and editing the pointed to object will change the object pointed to by both the original reference and its copy.

If you want to edit the actual value, you can use the index to access it. The following will work:

package main

import (
	"fmt"
)

func main() {
	slice := []string{"this", "is", "a", "slice", "of", "strings"}

	// Exclamation marks will be appended to the object in the slice.
	for index, _ := range slice {
		slice[index] = slice[index] + "!"
	}

	// They will be present in the output.
	for index, itemCopy := range slice {
		fmt.Printf("%v: %v\n", index, itemCopy)
	}
}

(Run in Playground)

Output:

0: this!
1: is!
2: a!
3: slice!
4: of!
5: strings!

Warning! This is not concurrency safe – in a more complex code base, there is no guarantee that another goroutine is not also iterating and editing the slice. You must add some syncronisation if you want to guarantee safety.

 

Leave a Reply

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