对nil map、nil slice的错误使用并不是很可怕,毕竟编译的时候就能发觉,下面要说的一个错误则非常坑爹,一不小心中招的话,很难排查。
2.误用:=赋值导致变量覆盖
先看下这段代码,猜猜会打印出什么:
package main
import (
"errors"
"fmt"
)
func main() {
i := 2
if i > 1 {
i, err := doDivision(i, 2)
if err != nil {
panic(err)
}
fmt.Println(i)
}
fmt.Println(i)
}
func doDivision(x, y int) (int, error) {
if y == 0 {
return 0, errors.New("input is invalid")
}
return x / y, nil
}
我估计有人会认为是:
1
1
实际执行一遍,结果是:
1
2
为什么会这样呢!?
这是因为golang中变量的作用域范围小到每个词法块(不理解的同学可以简单的当成 {} 包裹的部分)都是一个单独的作用域,大家都知道每个作用域的内部声明会屏蔽外部同名的声明,而每个 if 语句都是一个词法块,也就是说,如果在某个 if 语句中,不小心用 := 而不是 = 对某个 if 语句外的变量进行赋值,那么将产生一个新的局部变量,并仅仅在 if 语句中的这个赋值语句后有效,同名的外部变量会被屏蔽,将不会因为这个赋值语句之后的逻辑产生任何变化!
在语言层面这也许并不是个错误,但是实际工作中如果误用,那么产生的bug会很隐秘。比如例子中的代码,因为 err 是之前未声明的,所以使用了 := 赋值(图省事,少写了 var err error ),然后既不会在编译时报错,也不会在运行时报错,它会让你百思不得其解,觉得自己的逻辑明明走对了,为什么最后的结果却总是不对,直到你一点一点调试,才发现自己不小心多写了一个 : 。
我因为这个被坑过好几回了,每次都查了好久,以为是自己逻辑有漏洞,最后发现是把 = 写成了 := ,唉,说起来都是泪。
3.将值传递当成引用传递
值类型数据和引用类型数据的区别我相信在座的各位都能分得清,否则不用往下看了,因为看不懂。
在golang中, array 和 struct 都是值类型的,而 slice 、 map 、 chan 是引用类型,所以我们写代码的时候,基本不使用 array ,而是用 slice 代替它,对于 struct 则尽量使用指针,这样避免传递变量时复制数据的时间和空间消耗,也避免了无法修改原数据的情况。
如果对这点认识不清,导致的后果可能是代码有瑕疵,更严重的是产生bug。
考虑这段代码并运行一下:
package main
import "fmt"
type person struct {
name string
age byte
isDead bool
}
func main() {
p1 := person{name: "zzy", age: 100}
p2 := person{name: "dj", age: 99}
p3 := person{name: "px", age: 20}
people := []person{p1, p2, p3}
whoIsDead(people)
for _, p := range people {
if p.isDead {
fmt.Println("who is dead?", p.name)
}
}
}
func whoIsDead(people []person) {
for _, p := range people {
if p.age < 50 {
p.isDead = true
}
}
}









