One - One Code All

Blog Content

Go开发中常见的坑1

Go   2018-10-15 15:19:07

1.简短声明的变量只能在函数内部使用。

:=为简短声明

var为声明

2.使用简短声明来重复声明变量。

不能用简短声明方式来单独为一个变量重复声明, := 左侧至少有一个新变量,才允许多变量的重复声明。

one := 1

one, two := 2, 3


3.不能使用简短声明来设置结构体字段的值。

struct 的变量字段不能使用 := 来赋值,所以使用预定义的变量来避免解决。


4.不小心覆盖了变量。

可能会让人误会 := 是一个赋值操作符。如果你在新的代码块中误用了 :=,编译不会报错,但是变量不会按你的预期工作:变量只在其作用域内生效。

这是 Go 开发者常犯的错,而且不易被发现。

可使用 vet 工具来诊断这种变量覆盖,Go 默认不做覆盖检查,添加 -shadow 选项来启用:

go tool vet -shadow main.go

注意 vet 不会报告全部被覆盖的变量,可以使用 go-nyet 来做进一步的检测:

$GOPATH/bin/go-nyet main.go


5.显式类型的变量无法使用 nil 来初始化。

nil 是 interface、function、pointer、map、slice 和 channel 类型变量的默认初始值,注意这里没有string哦,它的默认初始值为空字符串。但声明时需要指定值为nil的数据类型,否则编译器无法推断出变量的具体类型。


6.直接使用值为 nil 的 slice、map。

允许对值为 nil 的 slice 添加元素,但对值为 nil 的 map 添加元素则会造成运行时 panic。


7.string 类型的变量值不能为 nil。

8.数组array类型的值作为函数参数(注意不是slice)

在 C/C++ 中,数组(名)是指针。将数组作为参数传进函数时,相当于传递了数组内存地址的引用,在函数内部会改变该数组的值。

在 Go 中,数组是值。作为参数传进函数时,传递的是数组的原始值拷贝,此时在函数内部是无法更新该数组的。

slice和数组在形式上的区别:数组[3]int{1,2,3},slice是[]int{1,2,3}。


9.range 遍历 slice 和 array。

Go 中的 range 在遍历时会生成 2 个值,第一个是元素索引,第二个是元素的值。


10.slice 和 array 其实是一维数据。

可以使用原始的一维数组、“独立“ 的切片、“共享底层数组”的切片来创建动态的多维数组。


独立的切片

使用“共享底层数组”的切片。


11.访问 map 中不存在的 key。

Go 会返回元素对应数据类型的零值,比如 nil、’’ 、false 和 0,取值操作总有值返回,故不能仅仅通过取出来的值是否为‘’或者nil来判断 key 是不是在 map 中。


12.string 类型的值是常量,不可更改。

string 类型的值本质上是只读的二进制 byte slice,如果真要修改字符串中的字符,将 string 转为 []byte 修改后,再转为 string 即可:

xBytes := []byte(strText)

xBytes[0] = 'T'


上边的示例并不是更新字符串的正确姿势,因为一个 UTF8 编码的字符可能会占多个字节,比如汉字就需要 3~4 个字节来存储,此时更新其中的一个字节是错误的。

更新字串的正确姿势:将 string 转为 rune slice(此时 1 个 rune 可能占多个 byte),直接更新 rune 中的字符

xBytes := []rune(strText1)

xBytes[0] = '我'


13.string 的长度

lenChar := utf8.RuneCountInString(char)


14.log.Fatal 和 log.Panic 不只是 log

log 标准库提供了不同的日志记录等级,与其他语言的日志库不同,Go 的 log 包在调用 Fatal*()、Panic*() 时能做更多日志外的事,如中断程序的执行等.


15. range 迭代 map

与map的底层相关

如果你希望以特定的顺序(如按 key 排序)来迭代 map,要注意每次迭代都可能产生不一样的结果。

Go 的运行时是有意打乱迭代顺序的,所以你得到的迭代结果可能不一致。但也并不总会打乱,得到连续相同的 5 个迭代结果也是可能的。


16. 按位取反

很多编程语言使用 ~ 作为一元按位取反(NOT)操作符,Go 重用 ^ XOR 操作符来按位取反,同时 ^ 也是按位异或(XOR)操作符。


Go 也有特殊的操作符 AND NOT &^ 操作符,不同位才取1。


17. (*)程序退出时还有 goroutine 在执行

程序默认不等所有 goroutine 都执行完才退出,这点需要特别注意。

如果你的 goroutine 要做消息的循环处理等耗时操作,可以向它们发送一条 kill 消息来关闭它们,比如直接关闭一个它们都等待接收数据的 channel。


一定要主要函数传参的时候传指针。


18. 向无缓冲的 channel 发送数据,只要 receiver 准备好了就会立刻返回

只有在数据被 receiver 处理时,sender 才会阻塞。因运行环境而异,在 sender 发送完数据后,receiver 的 goroutine 可能没有足够的时间处理下一个数据。


19. 向已关闭的 channel 发送数据会造成 panic

从已关闭的 channel 接收数据是安全的。

接收状态值 ok 是 false 时表明 channel 中已没有数据可以接收了。

类似的,从有缓冲的 channel 中接收数据,缓存的数据获取完再没有数据可取时,状态值也是 false。

向已关闭的 channel 中发送数据会造成 panic。


可使用一个废弃 channel done 来告诉剩余的 goroutine 无需再向 ch 发送数据。此时 <- done 的结果是 {}


20.使用了值为 nil 的 channel

在一个值为 nil 的 channel 上发送和接收数据将永久阻塞:



上一篇:python中打印输出详细异常信息traceback使用,异常的获取与处理
下一篇:mac查找my.cnf

The minute you think of giving up, think of the reason why you held on so long.