@@ -740,8 +740,11 @@ Go 如何复制map
740
740
testShareMap()
741
741
}
742
742
743
- 闭包问题
743
+ 循环变量与闭包问题 ⭐️
744
744
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
745
+ go 的循环变量是 per-loop 的而不是 per-iteration 绑定的,这个特性导致了很多隐蔽反直觉并且难以排查的bug。
746
+ go 官方目前仍在讨论是否要改善这个问题:https://github.com/golang/go/discussions/56010 。
747
+ 举一些常见例子防止踩坑:
745
748
746
749
.. code-block :: go
747
750
@@ -753,7 +756,6 @@ Go 如何复制map
753
756
)
754
757
755
758
// 闭包问题
756
-
757
759
func testClosure() {
758
760
data := []string{"one", "two", "three"}
759
761
for _, v := range data {
@@ -762,14 +764,14 @@ Go 如何复制map
762
764
}()
763
765
}
764
766
time.Sleep(1 * time.Second) // not good, just for demo
765
- // three three three
767
+ // three three three // 打印的都是最后一个值
766
768
}
767
769
768
- // 两种方式解决:1.使用一个for 循环临时变量
770
+ // 两种方式解决:1.使用一个块临时变量
769
771
func testClosure1() {
770
772
data := []string{"one", "two", "three"}
771
773
for _, v := range data {
772
- vcopy := v
774
+ vcopy := v // 使用一个临时变量,经常可以看到 v := v 这种写法
773
775
go func() {
774
776
fmt.Println(vcopy)
775
777
}()
@@ -778,7 +780,7 @@ Go 如何复制map
778
780
// one two three (may wrong order)
779
781
}
780
782
781
- // 方法2:使用传给匿名goroutine参数,推荐使用这种方式
783
+ // 方法2:使用传给匿名goroutine参数,个人推荐使用这种方式
782
784
func testClosure2() {
783
785
data := []string{"one", "two", "three"}
784
786
for _, v := range data {
@@ -790,6 +792,7 @@ Go 如何复制map
790
792
// one two three (may wrong order)
791
793
}
792
794
795
+ // 指针接收者调用场景
793
796
type field struct {
794
797
name string
795
798
}
@@ -808,12 +811,29 @@ Go 如何复制map
808
811
time.Sleep(1 * time.Second)
809
812
}
810
813
811
- func main() {
812
- // testClosure()
813
- // testClosure1()
814
- // testClosure2()
815
- testField()
814
+ // 结构体示例
815
+ values := []MyStruct{MyStruct{1}, MyStruct{2}, MyStruct{3}}
816
+ output := []*MyStruct{}
817
+ for _, v := range values {
818
+ // v := v
819
+ output = append(output, &v) // 不要直接引用一个循环变量的地址
816
820
}
821
+ fmt.Println("output: ", output) // 打印相同的地址
822
+
823
+ 总结一下:
824
+
825
+ - 不要直接对循环变量取地址
826
+ - 不要在 goroutine 中直接使用循环变量
827
+ - 如果循环变量(非指针)调用函数是一个指针接收者,调用值需要拷贝一个临时变量再调用
828
+
829
+ 解决方式:
830
+
831
+ 1. 创建一个临时局部变量(`v:=v `)
832
+ 2. 作为参数传入(解决goroutine场景)
833
+
834
+ 参考:
835
+
836
+ - https://nullprogram.com/blog/2014/06/06/ Per Loop vs. Per Iteration Bindings
817
837
818
838
Failed Type Assertions
819
839
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
0 commit comments