前言
从golang小白到成为golang工程师快两个月了,我要分享一下新手在开发中常犯的错误,都是我亲自踩过的坑。这些错误中有些会导致无法通过编译,这种错容易发现,而有些错误在编译时不会抛出,甚至在运行时也不会panic,如果缺少相关的知识,挠破头皮都搞不清楚bug出在哪。
1.对nil map、nil slice 添加数据
请考虑一下这段代码是否有错,然后运行一遍:
package main
func main() {
var m map[string]string
m["name"] = "zzy"
}
不出意外的话,这段代码将导致一个panic:
panic: assignment to entry in nil map
这是因为代码中只是声明了map的类型,却没有为map创建底层数组,此时的map实际上在内存中还不存在,即nil map,可以运行下面的代码进行验证:
package main
import "fmt"
func main() {
var m map[string]string
if m == nil {
fmt.Println("this map is a nil map")
}
}
所以想要顺利的使用map,一定要使用内建函数make函数进行创建:
m := make(map[string]string)
使用字面量的方式也是可以的,效果同make:
m := map[string]string{}
同样的,直接对nil slice添加数据也是不允许的,因为slice的底层也是数组,没有经过make函数初始化时,只是声明了slice类型,而底层数组是不存在的:
package main
func main() {
var s []int
s[0] = 1
}
上面的代码将产生一个panic runtime error:index out of range ,正确做法应该是使用make函数或者字面量:
package main
func main() {
//第二个参数是slice的len,make slice时必须提供,还可以传入第三个参数作为cap
s := make([]int, 1)
s[0] = 1
}
可能有人发现对nil slice使用append函数而不经过make也是有效的:
package main
import "fmt"
func main() {
var s []int
s = append(s, 1)
fmt.Println(s) // s => [1]
}
那是因为slice本身其实类似一个struct,它有一个len属性,是当前长度,还有个cap属性,是底层数组的长度,append函数会判断传入的slice的len和cap,如果len即将大于cap,会调用make函数生成一个更大的新数组并将原底层数组的数据复制过来(以上均为本人猜测,未经查证,有兴趣的同学可以去挑战一下源码),过程类似:
package main
import "fmt"
func main() {
var s []int //len(s)和cap(s)都是0
s = append(s, 1)
fmt.Println(s) // s => [1]
}
func append(s []int, arg int) []int {
newLen := len(s) + 1
var newS []int
if newLen > cap(s) {
//创建新的slice,其底层数组扩容为原先的两倍多
newS = make([]int, newLen, newLen*2)
copy(newS, s)
} else {
newS = s[:newLen] //直接在原数组上切一下就行
}
newS[len(s)] = arg
return newS
}









