19

GO常见的错误99%程序员会遇到

 5 years ago
source link: https://studygolang.com/articles/25966
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

新学习go语言的人可能遇到常见的错误,其中有两个比较常见的错误,需要单独拿出来说下,为什么要单独说呢,因为这两个错误跟其他语言不同,是因为go本身的设计造成的。

在循环(迭代)中使用了变量的引用

在go语言中,循环(迭代)所使用的变量是同一个变量,只是在每次循环的时候被赋于不同的值,这样的做的目的呢,当然是出于高效考虑咯。但是,如果使用不当的话,可能会引起意想不到的行为。

举一个栗子:

func main() {
	var out []*int
	for i := 0; i < 3; i++ {
		out = append(out, &i)
	}
	fmt.Println("Values:", *out[0], *out[1], *out[2])
	fmt.Println("Addresses:", out[0], out[1], out[2])
}

复制代码

上面的代码会输出:

Values: 3 3 3
Addresses: 0x40e020 0x40e020 0x40e020
复制代码

因为每次循环中,我们只是把变量 i 的地址放进 out 数组里,因为变量 i 是同一个变量,只有在循环结束的时候,被赋值为3。

解决方法:申明一个新的变量

for i := 0; i < 3; i++ {
	i := i // Copy i into a new variable.
 	out = append(out, &i)
 }
复制代码

结果

Values: 0 1 2
Addresses: 0x40e020 0x40e024 0x40e028
复制代码

同理对于切片来说,也用有这个问题,因为切片本身就只是一个地址而已

func main() {
	var out [][]int
	for _, i := range [][1]int{{1}, {2}, {3}} {
		out = append(out, i[:])
	}
	fmt.Println("Values:", out)
}
复制代码

结果:

Values: [[3] [3] [3]]
复制代码

同样的问题,在循环里使用协程也会遇到

在协程中使用循环变量

按照程序员的思维,都喜欢使用并发,你可能会写出下面的代码: 心里特别开心,原来go 的并发这么简单。

for _, val := range values {
	go func() {
    	fmt.Println(val)
	}()
}
复制代码

但是,你可能会发现输出的结果是一摸一样的! 因为go的协程跑起来也是需要一点时间的,循环结束的时候,可能一个goroute都没有跑完,然后 val 值确被赋值了,所以,你会看到,输出的都是最后一个值

解决方法:

for _, val := range values {
	go func(val interface{}) {
		fmt.Println(val)
	}(val)
}
复制代码

当然也可以

for i := range valslice {
	val := valslice[i]
	go func() {
		fmt.Println(val)
	}()
}
复制代码

Reference: github.com/golang/go/w…


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK