CuriousMind
CuriousMind

Reputation: 34145

Difference between for loop construct vs range keyword in go

Consider the following code which just prints all the ENV vars

package main

import (
    "fmt"
    "os"
)

func main() {
    for i, env := range os.Environ() {
        fmt.Println(i, env)
    }
}

Here, os.Environ() is supposed to return array of strings([] string), to loop over it. I need to to use range keyword & also for loop. Question is:

  1. Why are both for & range required? is it possible to use for loop for this as []string is already an array & we can iterate over arrays right?
  2. In the above code what does range do? and what does for loop does?

Sorry if this question is too stupid, I am just starting with Go

Upvotes: 6

Views: 6086

Answers (3)

hookenz
hookenz

Reputation: 38907

I find the accepted answer slightly confusing as it doesn't directly directly address the questions.

So to answer your questions.

  1. for range is one loop style you can use to access the elements in the environment slice returned from os.Environ(), but as I show below you can use the other for loop style. You will see the differences below.
  2. range in this for loop is the construct you use that will provide a copy and the index for each of the elements in the slice.

To summarize, there are basically two forms of the for loop construct in go (actually 3, but we're only concerned with two here).

One uses the range keyword, and the other does not.

The for ... range version creates a copy of the item in the slice you are iterating over. The other, does not and you must access it by reference.

func main() {
    for i, env := range os.Environ() {
        fmt.Println(i, env)
    }
}

Here is how you would write the for loop without using range construct:

func main() {
    env := os.Environ()
    for i := 0; i < len(env); i++ {
        fmt.Println(i, env[i])
    }
}

I also found a third option which looks tidy and has the same advantage as option 2.

func main() {
    env := os.Environ()
    for i := range env {
        fmt.Println(i, env[i])
    }
}

The first approach is nice and clean, but the second provides better performance because it doesn't do a copy. Both have their place. I would generally use range, but where you need it, the traditional C-like loop is available.

Note: Whenever you are dealing with strings you should always use the range loop as it will return a rune instead of a char. This is particularly important when dealing with internationalisation. See this blog post about string iteration here: Strings, bytes, runes and characters in Go

Upvotes: 2

Nik
Nik

Reputation: 39

One notable difference if for range creates copy of each the objects from slice/array. Have a look at following code snippet. https://go.dev/play/p/MHaCXj-Y5WD

Upvotes: 1

VonC
VonC

Reputation: 1324576

As mentioned in Range Clauses:

A range clause provides a way to iterate over a array, slice, string, map, or channel.

If you want to iterate over an []string, you need range.

A For statement doesn't always use range.

ForStmt = "for" [ Condition | ForClause | RangeClause ] Block .

You have:

  • In its simplest form, a "for" statement specifies the repeated execution of a block as long as a boolean condition evaluates to true

  • A "for" statement with a ForClause is also controlled by its condition, but additionally it may specify an init and a post statement, such as an assignment, an increment or decrement statement

  • A "for" statement with a "range" clause iterates through all entries of an array, slice, string or map, or values received on a channel. For each entry it assigns iteration values to corresponding iteration variables if present and then executes the block.

Upvotes: 8

Related Questions