0%

Golang不可不知的十大陷阱(一)- for循环取地址

俗话说得好,golang一时爽,一直golang一直爽.

最近公司也在从Python逐渐迁往Golang,在迁移的过程中不得不感叹这世界还有golang这么吊吊的语言,完全就是爱用就用不用就拉到的架势,让人又爱又恨.

今天开始我们来慢慢盘点下踩到的那些神级坑。 第一章,我们从for循环取地址的神级坑开始。


今天天气灰常的好,心情不错,小明来到了公司,这时候他的同事小亮告诉他昨天的function已经ready可以调用了,小明蹦蹦跳跳的调用了下函数:

1
2
3
4
5
6
7
8
9
10
11
type DemoStruct struct {
DemoValue string `json:"demo_value"`
}

func XiaoLiangFunction() ([]DemoStruct, error) {
demoArray := make([]DemoStruct, 3)
demoArray[0] = DemoStruct{DemoValue: "1"}
demoArray[1] = DemoStruct{DemoValue: "2"}
demoArray[2] = DemoStruct{DemoValue: "3"}
return demoArray, nil
}

但是小明习惯用指针,想把返回值[]DemoStruct转换成[]*DemoStruct。

心中默念了声**后,小明开始了一天的劳作,终于在下班前:

1
2
3
4
5
6
7
8
9
10
11
func main() {

demoArray, _ := XiaoLiangFunction()
demoArrayAddress := make([]*DemoStruct, 3)

for index, demoValue := range demoArray {
demoArrayAddress[index] = &demoValue
}

fmt.Println(demoArrayAddress)
}

背上书包,click run,准备在结果出现的那一刻潇洒转身,差点儿闪了自己的老腰:

1
[0xc000010200 0xc000010200 0xc000010200]

居然数组所有指针元素的地址都是一模一样的!!

这时,夕阳西下,小明又开始了新一晚的劳作。

why?

其实这个bug并不难理解,循环变量demoValue在生成后,之后的循环一直被重复调用,所以才产生这个结果。

最简单的解法是在取地址前加一行demoValue := demoValue,最终的结果就是

1
2
3
4
5
6
7
8
9
10
11
12
func main() {

demoArray, _ := XiaoLiangFunction()
demoArrayAddress := make([]*DemoStruct, 3)

for index, demoValue := range demoArray {
demoValue := demoValue
demoArrayAddress[index] = &demoValue
}

fmt.Println(demoArrayAddress)
}

看起来是不是不是很优雅?其实这是golang官方推荐的解法

详情见:https://github.com/golang/go/wiki/CommonMistakes

看,golang官方其实知道这是个常见的错误,所以专门开辟了一个wiki page来讲述这个common mistakes,然后还给出解法,但是感觉还是有点儿反人类。

such is life.