Go > Core Go Basics > Control Flow > Range loops

Iterating with Range in Go

This example demonstrates how to use the range keyword to iterate over various data structures in Go, including arrays, slices, strings, and maps. Understanding range is crucial for efficiently processing collections of data.

Basic Range Loop over a Slice

This code iterates over a slice of integers named numbers. The range keyword returns both the index and the value for each element in the slice. The fmt.Printf function then prints the index and value to the console.

package main

import "fmt"

func main() {
    numbers := []int{2, 4, 6, 8, 10}

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

Omitting the Index or Value

In Go, if you don't need the index or the value, you can omit it using the blank identifier _. If you only want the index, you can simply assign the range result to a single variable, which will be the index. This avoids unnecessary variable declarations.

package main

import "fmt"

func main() {
    numbers := []int{2, 4, 6, 8, 10}

    // Omitting the index using the blank identifier '_'
    for _, value := range numbers {
        fmt.Printf("Value: %d\n", value)
    }

    // Omitting the value - only index is used
    for index := range numbers {
        fmt.Printf("Index: %d\n", index)
    }
}

Iterating over a String

When iterating over a string with range, it iterates over Unicode code points (runes), not bytes. Each iteration provides the starting byte index of the rune and the rune value itself. The %c format specifier is used to print the rune as a character, and %U prints the Unicode value.

package main

import "fmt"

func main() {
    message := "Hello, Go!"

    for index, runeValue := range message {
        fmt.Printf("Index: %d, Rune: %c (Unicode: %U)\n", index, runeValue, runeValue)
    }
}

Iterating over a Map

When iterating over a map, range returns the key and the value for each entry. The order of iteration is not guaranteed to be the same each time you run the code. Maps are inherently unordered data structures.

package main

import "fmt"

func main() {
    studentAges := map[string]int{
        "Alice":   20,
        "Bob":     22,
        "Charlie": 21,
    }

    for name, age := range studentAges {
        fmt.Printf("Name: %s, Age: %d\n", name, age)
    }
}

Concepts behind the snippet

The range keyword provides a convenient way to iterate over elements in arrays, slices, strings, and maps. It abstracts away the details of manual indexing and provides a clean and readable syntax. It simplifies data structure traversal.

Real-Life Use Case

Imagine you're building a web application that processes user data. You might use a range loop to iterate over a slice of user objects, perform validation on each user's data, and store the results in a database. Or processing HTTP headers from a request.

Best Practices

  • Avoid modifying the underlying data structure while iterating over it with range, as this can lead to unpredictable behavior.
  • Use the blank identifier _ to discard values you don't need, improving code clarity and potentially reducing memory usage.
  • Be mindful of the type of data you're iterating over (e.g., runes vs. bytes when iterating over strings).

Interview Tip

Be prepared to explain the behavior of range with different data types. Understand how it handles strings (runes vs. bytes) and how it iterates over maps (unordered).

When to use them

Use range loops when you need to iterate over a collection of data and perform some operation on each element. It is preferable to traditional for loops when you need both the index and the value or when you want a more concise syntax.

Memory footprint

The memory footprint of a range loop is generally small, especially when working with slices and arrays. However, when iterating over large maps or strings, consider the memory implications of copying the data if you are making modifications within the loop. In most common scenarios, the performance overhead is negligible.

alternatives

Alternatives to range include traditional for loops with explicit indexing. For example: for i := 0; i < len(mySlice); i++. However, range is often more readable and less error-prone, especially when dealing with complex data structures.

pros

  • Concise and readable syntax.
  • Automatically handles index and value retrieval.
  • Works with various data types (arrays, slices, strings, maps).
  • Reduces the risk of off-by-one errors.

cons

  • Can be less efficient than traditional for loops in certain edge cases (rare).
  • Modifying the underlying data structure during iteration can lead to unexpected behavior.

FAQ

  • What happens if I modify the slice inside a range loop?

    Modifying the slice (e.g., appending or deleting elements) during a range loop can lead to unpredictable behavior, potentially causing the loop to skip elements or enter an infinite loop. It's best to avoid modifying the slice within the loop or to create a copy of the slice before iterating.
  • Does the order of iteration matter when ranging over a map?

    No, the order of iteration is not guaranteed to be the same each time you run the code when ranging over a map. Maps are inherently unordered data structures.