概述
在编写 Go 代码的时候,由于语言的特性会遇到一些难以理解的简单错误,所以我就写了这篇文章用于记录我遇到的问题的集合。
time
mismatched types int32 and time.Duration)
今天在写一段 Go 代码的时候,居然报了一个意料之外的错:
[root@liqiang.io]# cat test.go
time.sleep(time.Second * rand.Int())
然后提示错误:
invalid operation: rand.Int31n(1000) * time.Millisecond (mismatched types int32 and time.Duration)
这就很蒙了,明明:time.Second * 1
都是正常的,为什么这样不行?于是就找了找,发现需要将 int 转换成 Duration,这个没理解为什么?(updated at:2020-10-23,我在这篇文章中介绍了为什么:Go 语言中的常量)
[root@liqiang.io]# cat test.go
time.Sleep(time.Second * time.Duration(rand.Int())
这样就对了。
string
字符串的长度
需要注意的是如果使用 len()
函数,返回的是字符串的 byte 长度,如果这个字符串是 Unicode(UTF-8),那么 len(string)
的值就通常不是字符的个数,而是字节个数:
[root@liqiang.io]# cat main.go
func main() {
fmt.Println(len("世界")) // 输出是:6
fmt.Println(len([]rune("世界"))) // 输出是:2
for _, ch := range "世界" {
// 将会循环两次
// 第一次是 ”19990 世“
// 第二次是”30028 界“
fmt.Println(ch, string([]rune{ch}))
}
}
string <-> float
String 转换为 Float(适用于各种类型)
[root@liqiang.io]# cat test.go
f := "3.14159265"
if s, err := strconv.ParseFloat(f, 32); err == nil {
fmt.Println(s) // 3.1415927410125732
}
if s, err := strconv.ParseFloat(f, 64); err == nil {
fmt.Println(s) // 3.14159265
}
Float 转换为 String
性能比较差的做法
[root@liqiang.io]# cat test.go
s := fmt.Sprintf("%f", 123.456) // s == "123.456000"
性能更好的做法
[root@liqiang.io]# cat test.go
strconv.FormatFloat(f, 'g', -1, 32)
去除字符串中非 Ascii 字符
[root@liqiang.io]# cat test.go
func main() {
example := "#GoLangCode!$!"
// Make a Regex to say we only want letters and numbers
reg, err := regexp.Compile("[^a-zA-Z0-9]+")
if err != nil {
log.Fatal(err)
}
processedString := reg.ReplaceAllString(example, "")
fmt.Printf("A string of %s becomes %s \n", example, processedString)
}
uint64 -> String
在 Go 语言中将 uint64 转换成 string 类型:
[root@liqiang.io]# cat uint64.go
var myNumber uint64
myNumber = 18446744073709551615
str := strconv.FormatUint(myNumber, 10)
bytes
bytes 转为 Reader
[root@liqiang.io]# cat test.go
r := bytes.NewReader(byteData)
数字
Float64 的 Nan 和 Inf
注意这段代码:
[root@liqiang.io]# cat nan.go
nan := math.NaN()
pos_inf := math.Inf(1)
neg_inf := math.Inf(-1)
fmt.Println(nan == nan) // false
fmt.Println(pos_inf == pos_inf) // true
fmt.Println(neg_inf == neg_inf) // true
fmt.Println(pos_inf == neg_inf) // false
结构体
结构体指针和非指针的差异
[root@liqiang.io]# cat demo.go
type A struct{
lock sync.Mutex
}
pass(a A) {} // 传值,会拷贝 lock,这是不安全的
for _, a := range []A{}{} // 每次迭代都会拷贝 lock,不安全
for _, a := range []*A{}{} // ok 的,安全
其他
failed to restore the stack
在使用 -race
模式进行单测时,遇到了 data race 的问题,但是,其中给一个位置的报错居然是:
[root@liqiang.io]# cat error.log
110Write at 0x00c0007311f8 by goroutine 71:
111 xxxxxxxxxxxxx.keepRegisterLoop()
112 /builds/xxxxxx:1020 +0x29d
113Previous read at 0x00c0007311f8 by goroutine 39:
114 [failed to restore the stack]
这让我蒙了,为什么会这样呢?查了一下资料之后发现这是 Go 的一些局限,后来发现原来是因为我用了反射,所以导致这里无法获取到具体的 stack,解决办法是:
- Ref: runtime: failed to restore the stack #10661
- run with GORACE=history_size=7 (as per manual https://golang.org/doc/articles/race_detector.html)
- reduce test size in various aspects
- run the test a million of times grepping for the second stack (e.g. using the stress utility)
我使用了第一种方式就找到了问题,就是因为使用了反射的缘故。