func main() {x := 1println(x)// 1{println(x)// 1x := 2println(x)// 2// 新的 x 变量的作用域只在代码块内部}println(x)// 1}
这是 Go 开发者常犯的错 , 而且不易被发现 。可使用 vet工具来诊断这种变量覆盖 , Go 默认不做覆盖检查 , 添加 -shadow 选项来启用:
> go tool vet -shadow main.gomain.go:9: declaration of "x" shadows declaration at main.go:5
注意 vet 不会报告全部被覆盖的变量 , 可以使用 go-nyet 来做进一步的检测:
> $GOPATH/bin/go-nyet main.gomain.go:10:3:Shadowing variable `x`
8.显式类型的变量无法使用 nil 来初始化nil 是 interface、function、pointer、map、slice 和 channel 类型变量的默认初始值 。但声明时不指定类型 , 编译器也无法推断出变量的具体类型 。
// 错误示例func main() {var x = nil// error: use of untyped nil_ = x}// 正确示例func main() {var x interface{} = nil_ = x}
9.直接使用值为 nil 的 slice、map允许对值为 nil 的 slice 添加元素 , 但对值为 nil 的 map添加元素则会造成运行时 panic
// map 错误示例func main() {var m map[string]intm["one"] = 1// error: panic: assignment to entry in nil map// m := make(map[string]int)// map 的正确声明 , 分配了实际的内存}// slice 正确示例func main() {var s []ints = append(s, 1)}
10.map 容量在创建 map 类型的变量时可以指定容量 , 但不能像 slice 一样使用 cap() 来检测分配空间的大小:
// 错误示例func main() {m := make(map[string]int, 99)println(cap(m))// error: invalid argument m1 (type map[string]int) for cap}
11.string 类型的变量值不能为 nil对那些喜欢用 nil 初始化字符串的人来说 , 这就是坑:
// 错误示例func main() {var s string = nil// cannot use nil as type string in assignmentif s == nil {// invalid operation: s == nil (mismatched types string and nil)s = "default"}}// 正确示例func main() {var s string// 字符串类型的零值是空串 ""if s == "" {s = "default"}}
12.Array 类型的值作为函数参数在 C/C++ 中 , 数组(名)是指针 。将数组作为参数传进函数时 , 相当于传递了数组内存地址的引用 , 在函数内部会改变该数组的值 。
在 Go 中 , 数组是值 。作为参数传进函数时 , 传递的是数组的原始值拷贝 , 此时在函数内部是无法更新该数组的:
// 数组使用值拷贝传参func main() {x := [3]int{1,2,3}func(arr [3]int) {arr[0] = 7fmt.Println(arr)// [7 2 3]}(x)fmt.Println(x)// [1 2 3]// 并不是你以为的 [7 2 3]}
如果想修改参数数组:
- 直接传递指向这个数组的指针类型:
// 传址会修改原数据func main() {x := [3]int{1,2,3}func(arr *[3]int) {(*arr)[0] = 7fmt.Println(arr)// &[7 2 3]}(&x)fmt.Println(x)// [7 2 3]}
- 直接使用 slice:即使函数内部得到的是 slice 的值拷贝 , 但依旧会更新 slice 的原始数据(底层 array)
// 会修改 slice 的底层 array , 从而修改 slicefunc main() {x := []int{1, 2, 3}func(arr []int) {arr[0] = 7fmt.Println(x)// [7 2 3]}(x)fmt.Println(x)// [7 2 3]}
13.range 遍历 slice 和 array 时混淆了返回值与其他编程语言中的 for-in 、foreach 遍历语句不同 , Go 中的 range 在遍历时会生成 2 个值 , 第一个是元素索引 , 第二个是元素的值:// 错误示例func main() {x := []string{"a", "b", "c"}for v := range x {fmt.Println(v)// 1 2 3}}// 正确示例func main() {x := []string{"a", "b", "c"}for _, v := range x {// 使用 _ 丢弃索引fmt.Println(v)}}
14.slice 和 array 其实是一维数据看起来 Go 支持多维的 array 和 slice , 可以创建数组的数组、切片的切片 , 但其实并不是 。
经验总结扩展阅读
- 床单洗完晾干变臭是为啥
- 厨房为什么有蚂蚁
- 完 golang开发:go并发的建议
- 毛巾用几天变一块块黄
- 蟑螂危害
- 缺少勇气,这辈子不可能主动恋爱的星座
- 为什么手机使用一会儿就很烫
- 鼻炎可能导致耳鸣吗
- 低血钙是什么意思
- 可能和哪首歌很像