0
package main

import (
    "fmt"
)

func main() {
    a := []int{1, 2, 3}
    var fs []func()
    for idx, x := range a {
        f := func() {
            fmt.Println(idx, " ", x)
        }
        fs = append(fs, f)

    }
    for _, f := range fs {
        f()
    }
}

I have this piece of code. The output of this code is

2 3
2 3
2 3

Which isn't matched my expectation

0 1
1 2
2 3

As I guest is that after the for loop, the ref of idx and x will point to the last element of array a and the function is executed after this for loop so it will print out 2 3 for an fs[i].

2 Answers 2

2

The functions refer to the single idx and x variables. The variables are modified on each step through the loop and have the values 2 and 3 when the loop exits.

Fix by declaring variables for each function:

for idx, x := range a {
    idx := idx // <-- add this line and next
    x := x
    f := func() {
        fmt.Println(idx, " ", x)
    }
    fs = append(fs, f)
}

The inner idx and x variables are set once with the values you expected.

This issue is covered in the FAQ.

Sign up to request clarification or add additional context in comments.

2 Comments

Great, is it good practice when using the same variable name like idx := idx ?
I think think it is a good practice use the same name, but some people do not like shadowed variables (the inner variables shadow the variables declared in the for range. For what it's worth, the last example in the FAQ entry on closures uses the same name.
1

This is due do a concept called closure. Basically the idx and x variable that you are using inside the function f is using the outside variables. So when the function is actually called it still "refers" to the outside variables which has be iterated to the end. There are many solution to fix this, for your code one fix is to assign new variables inside the for loop. In the code below a and b takes copy for each iteration and use the same when function is invoked.

package main

import (
    "fmt"
)

func main() {
    a := []int{1, 2, 3}
    var fs []func()
    for idx, x := range a {
        a := idx
        b := x

        f := func() {
            fmt.Println(a, " ", b)
        }
        fs = append(fs, f)

    }
    for _, f := range fs {
        f()
    }
}

This code will produce the output

0   1
1   2
2   3

You can see some details in here.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.