要在for循环中使用goroutine,为什么要遍历指向struct而不是struct自身的指针

Background

I'm reading 50 shades in Go, specifically Iteration Variables and Closures in "for" Statements, and I'm going to take an excerpt from it.

Incorrect

    package main

    import (  
        "fmt"
        "time"
    )

    type field struct {  
        name string
    }

    func (p *field) print() {  
        fmt.Println(p.name)
    }

    func main() {  
        data := []field{{"one"},{"two"},{"three"}}

        for _,v := range data {
            go v.print()
        }

        time.Sleep(3 * time.Second)
        //goroutines print: three, three, three
    }

Correct

Change []field{{"one"},{"two"},{"three"}} to []*field{{"one"},{"two"},{"three"}}, and one, two, and three will be printed in some order.

My Thought Process

  1. In the incorrect one, go v.print() is replaced by go (&v).print() by compiler because print() is defined on pointer receiver.

  2. Until the spawned goroutine is executed, runtime only knows that goroutine should executeprint(), but has no idea pointer of which instance should be passed as the receiver.

  3. When the spawned goroutine is executed, it's highly possible that the for loop has terminated, so when we want to decide which value should be passed as the receiver, we get the address of v, which is shared during the entire loop and updated in each iteration, so we pass the address of the last element of data as the receiver to print(), and that's why we get 3 three printed.

Problem

To me, changing []field to []*field only lets compiler skip the step 1, but does not change step 2 and step 3, so I don't know why that fixes the problem.

I guess there must be some flaws in my thought process, and I appreciate any advice.

Update

I happened to see another correct implementation here, and I think I might know where went wrong in my thought process.

data := []field{{"one"}, {"two"}, {"three"}}

for i := range data {
    go data[i].print()
}

The thing is, the pointer to be passed to print() as the receiver, is determined in step 2 instead of step 3. That means in the incorrect version, we're passing the same address in each iteration, but the content it points to (data) is updated in each iteration. However, in the correct version, the pointers being passed to print() as the receiver, point to the actual elements of field. The same applies to the case using indices.

Your receiver is a poniter and you have to define your field slice as pointer this is why this code works

data := []*field{{"one"}, {"two"}, {"three"}}

if you change your receiver to non pointer you code works too .

func (p field) print() {
    fmt.Println(p.name)
}

data := []field{{"one"}, {"two"}, {"three"}}