A question about an example in github recent discussion "redefining for loop variable semantics"

In the recent discussion redefining for loop variable semantics, Rob gave an example and he think it is unecessary to copy the for loop variable when it is an interface value. For convenience, the example and Rob’s statement are mirrored here:

 	for _, informer := range c.informerMap {
+		informer := informer
 		go informer.Run(stopCh)
 	}

Rob said,

One of these two changes is unnecessary and the other is a real bug fix, but you can’t tell which is which without more context. (In one, the loop variable is an interface value, and copying it has no effect;

Acutually, from my opinion, it is still necessary when the loop variable is an interface value. I did a test based on the following code.

type Inf interface {
	Get()
}


type S struct {
	name string
}

func (s *S) Get()  {
	fmt.Println(s)
	return
}

func main() {
	infs := []Inf{&S{name: "Alice"}, &S{name: "Bob"}}

	var wg sync.WaitGroup
    // i is an interface value
	for _, i := range infs {
		// i := i
		wg.Add(1)
		go func() {
			defer wg.Done()
			i.Get()
		}()
	}
	wg.Wait()
}

Without copying the loop variable to a local variable, the output is the following, which is wrong.

&{Bob}
&{Bob}

I may misunderstand what Rob said. Could you please help me with the question that what does “he loop variable is an interface value, and copying it has no effect” mean?

Here’s a longer copy of what you mean with some more context around it:

I ran a program to process the git logs of the top 14k modules, from about 12k git repos and looked for commits with diff hunks that were entirely “x := x” lines being added. I found about 600 such commits. On close inspection, approximately half of the changes were unnecessary, done probably either at the insistence of inaccurate static analysis, confusion about the semantics, or an abundance of caution. Perhaps the most striking was this pair of changes from different projects:

 	for _, informer := range c.informerMap {
+		informer := informer
 		go informer.Run(stopCh)
 	}
 	for _, a := range alarms {
+		a := a
 		go a.Monitor(b)
 	}

One of these two changes is unnecessary and the other is a real bug fix, but you can’t tell which is which without more context. (In one, the loop variable is an interface value, and copying it has no effect; in the other, the loop variable is a struct, and the method takes a pointer receiver, so copying it ensures that the receiver is a different pointer on each iteration.)

I think this is an example that demonstrates the problem: The Go Play Space (same thing on normal Go Playground: Go Playground - The Go Programming Language)

It has taken me a few days to put it together because of, as Russ Cox said, “confusion about the semantics” (that is, my confusion).

I think this example demonstrates the problem, but I have to admit that I don’t actually understand how it works :disappointed:. Maybe (???) it is because goroutines started from interface method calls “capture” or “close over” the interface by value, but goroutines started from non-interface method calls capture the value by reference?

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.