go常见的坑
1、 for循环中使用短变量声明初始值
案例1:
type Data struct { d *int } func main() { list := make([]Data, 0) for i := 0; i < 10; i++ { list = append(list, Data{ d: &i}) } for _, item := range list { fmt.Printf("%d ",*item.d) } fmt.Println() }
预期输出:
0 1 2 3 4 5 6 7 8 9
实际输出:
10 10 10 10 10 10 10 10 10 10
原因是在Go中,变量声明之后,在同一作用域中,对于同一变量名,后续的声明是不会分配新的变量(即没有新的地址空间),所以这里的 i 的内存地址始终不变。因此每次变量 i 指向的地址值 最终为10。对于该问题,使用一个新的变量存储,然后再想slice中append临时变量的地址地址即可,如下:
for i := 0; i < 10; i++ { temp := i list = append(list, Data{ d: &temp}) }
案例2:
for-range中初始化使用短变量
func main() { sl := []int{1,2,3,4,5,6,7,8,9} list := make([]*int, len(sl)) for i, v := range sl{ list[i] = &v } fmt.Println(list) }
此处list中元素的地址都是指向0xc00012a000,其原因与上述一致。解决方法也是使用一个临时变量存储即可
2、go并发的坑 -闭包陷阱
案例1:
package main import ( "fmt" "sync" "time" ) func main() { wg := sync.WaitGroup{} wg.Add(2) for i := 0; i < 2; i++ { go func() { prefix := fmt.Sprintf("%d", i+1) for c := 'A'; c <= 'A'; c++ { fmt.Printf("%s:%c\n", prefix, c) time.Sleep(time.Millisecond) } wg.Done() }() } fmt.Println("Card") wg.Wait() }
输出如下:
案例2:
package main import ( "fmt" "runtime" "time" ) func main() { var a [10]int for i := 0; i < 10; i++ { go func() { for { a[i]++ runtime.Gosched() } }() } time.Sleep(time.Millisecond) fmt.Println("a", a) }
案例3:
func main() { var wg sync.WaitGroup sl := []int{1,2,3,4,5,6,7,8,9} for i, v := range sl{ go func() { defer wg.Done() fmt.Println(i, v) }() wg.Add(1) } wg.Wait() }
实际输出如下:
8 9 8 9 8 9 8 9 8 9 8 9 8 9 8 9 8 9
这里输出的结果,i, v都是一样的,其原理和上面一样,也是由于变量只会在第一次声明时初始化。这里解决办法是直接使用闭包函数传参,将i,v作为参数传递进去(会进行拷贝),从而达到预期效果,改进代码如下:
go func(i, v int) { // 注意这里的参数变化 defer wg.Done() fmt.Println(i, v) }(i, v)
3、defer的坑 -闭包陷阱
案例1、
package main import "fmt" func main() { for i := 3; i > 0; i-- { defer func() { fmt.Print(i, " ") }() } }
问题解析:这里是极度容易踩坑的地方,由于defer这里调用的func没有参数,等执行的时候,i已经为0(按3 2 1逆序,最后一个i=1时,i--的结果最后是0),所以这里输出3个0 。
如果还不理解:
package main import "fmt" func main() { for i := 3; i > 1; i-- { // 循环满足条件的是 3 2, defer func() { // 因为func 没有参数,defer运行最后i--即 2-- 结果为 1 fmt.Print(i, " ") // 循环2次 结果均为 1 }() } }//输出 1 1