1、如何在for range 中使用 闭包 ?
我们先看一个示例:
1. package main
2.
3. import (
4. “fmt”
5. “time”
6. )
7.
8. type field struct {
9. name string
10. }
11.
12. func (p *field) print() {
13. fmt.Println(p.name)
14. }
15.
16. func main() {
17. data := []field{{“a”},{“b”},{“c”}}
18.
19. for _,v := range data {
20. go v.print()
21. }
22.
23. time.Sleep(3 * time.Second)
24. }
该示例输出结果为:
2、问题思考
为什么输出的结果不是 a, b, c 呢?
在 go v.print() 前面添加 fmt.Printf(“&v=%p v=%s\n”, &v, v),打印一下 v的地址, 输出结果为:
打印出 v 的地址都是相同的。
for _,v := range data { // for语句中的迭代变量(如: v)在每次迭代时被重新使用, 一直复用。
go v.print() // 此处可理解为: go (&v).print(), 也就是用 v 的指针去调用,而且 v 会在每次迭代时复用, 所以每一个调用的 receiver 都是共同指向 v 的指针, 而且v在最后一次迭代后, 被赋值为:”c”, 所以才有了打印出3个”c”的结果。
3、解决方案
方法一: data 声明为指针类型
代码如下:
1. package main
2.
3. import (
4. “fmt”
5. “time”
6. )
7.
8. type field struct {
9. name string
10. }
11.
12. func (p *field) print() {
13. fmt.Println(p.name)
14. }
15.
16. func main() {
17. data := []*field{{“a”}, {“b”}, {“c”}}
18.
19. for _, v := range data {
20. go v.print() //v本身就是指针,指向a,b,c; 迭代时会改变指向,直接调用没有复制,故每次调用时v都是分别指向a,b,c的地址值
21. }
22.
23. time.Sleep(3 * time.Second)
24. }
方法二: for 循环中保存当前迭代变量的值
代码如下:
1. package main
2.
3. import (
4. “fmt”
5. “time”
6. )
7.
8. type field struct {
9. name string
10. }
11.
12. func (p *field) print() {
13. fmt.Println(p.name)
14. }
15.
16. func main() {
17. data := []field{{“a”}, {“b”}, {“c”}}
18.
19. for _, v := range data {
20. temp := v //保存当前迭代变量
21. go temp.print()
22. }
23.
24. time.Sleep(3 * time.Second)
25. }
4、总结
当方法定义时为Pointer receiver
如: func (p *field) print() 时
(1) Pointer Receive不复制,所以当以值方式调用时,直接 &value取地址作为pointer receiver;
(2)若for _,v := range, data中的v本身就是指针, 则直接调用;
(3)for range会在每次迭代中一直复用迭代变量(如: v)。