Фон
Я читаю 50 оттенков в Go, в частности Переменные итераций итераций в "for" утверждениях , и я собираюсь взять отрывок из этого.
Неверно
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
}
Правильно
Измените []field{{"one"},{"two"},{"three"}}
на []*field{{"one"},{"two"},{"three"}}
, и one
, two
и three
будут напечатаны в некотором порядке.
Процесс моей мысли
В неправильном случае go v.print()
заменяется на go (&v).print()
компилятором, поскольку print()
определено в приемнике указателя.
До тех пор, пока не будет запущена порожденная программа, среда выполнения знает только, что она должна выполнить print()
, но не имеет понятия, какой экземпляр должен быть передан в качестве получателя.
Когда запускается порожденная программа, весьма вероятно, что цикл for завершился, поэтому, когда мы хотим решить, какое значение следует передать в качестве получателя, мы получаем адрес v
, который используется совместно во времявесь цикл и обновляется в каждой итерации, поэтому мы передаем адресess последнего элемента data
в качестве получателя print()
, и поэтому мы получаем 3 three
напечатанных.
Задача
Для меня,изменение []field
на []*field
только позволяет компилятору пропустить шаг 1, но не изменяет шаг 2 и шаг 3, поэтому я не знаю, почему это решает проблему.
Я полагаю, что должны быть некоторыенедостатки в моем мыслительном процессе, и я ценю любые советы.
Обновление
Мне довелось увидеть другую правильную реализацию здесь , и я думаю, что могу знать, где что-то пошло не такмой мыслительный процесс.
data := []field{{"one"}, {"two"}, {"three"}}
for i := range data {
go data[i].print()
}
Дело в том, что указатель, который должен быть передан print()
в качестве получателя, определяется на шаге 2 вместо шага 3. Это означает, что в неправильной версии мыпередача одного и того же адреса в каждой итерации, но содержимое, на которое он указывает (data
), обновляется в каждой итерации.Однако в правильной версии указатели, передаваемые в print()
в качестве получателя, указывают на фактические элементы field
.То же относится и к случаю с использованием индексов.