概述

在编写 Go 代码的时候,由于语言的特性会遇到一些难以理解的简单错误,所以我就写了这篇文章用于记录我遇到的问题的集合。

time

mismatched types int32 and time.Duration)

今天在写一段 Go 代码的时候,居然报了一个意料之外的错:

  1. [root@liqiang.io]# cat test.go
  2. time.sleep(time.Second * rand.Int())

然后提示错误:

  1. invalid operation: rand.Int31n(1000) * time.Millisecond (mismatched types int32 and time.Duration)

这就很蒙了,明明:time.Second * 1 都是正常的,为什么这样不行?于是就找了找,发现需要将 int 转换成 Duration,这个没理解为什么?(updated at:2020-10-23,我在这篇文章中介绍了为什么:Go 语言中的常量

  1. [root@liqiang.io]# cat test.go
  2. time.Sleep(time.Second * time.Duration(rand.Int())

这样就对了。

string

字符串的长度

需要注意的是如果使用 len() 函数,返回的是字符串的 byte 长度,如果这个字符串是 Unicode(UTF-8),那么 len(string) 的值就通常不是字符的个数,而是字节个数:

  1. [root@liqiang.io]# cat main.go
  2. func main() {
  3. fmt.Println(len("世界")) // 输出是:6
  4. fmt.Println(len([]rune("世界"))) // 输出是:2
  5. for _, ch := range "世界" {
  6. // 将会循环两次
  7. // 第一次是 ”19990 世“
  8. // 第二次是”30028 界“
  9. fmt.Println(ch, string([]rune{ch}))
  10. }
  11. }

string <-> float

  1. String 转换为 Float(适用于各种类型)

    1. [root@liqiang.io]# cat test.go
    2. f := "3.14159265"
    3. if s, err := strconv.ParseFloat(f, 32); err == nil {
    4. fmt.Println(s) // 3.1415927410125732
    5. }
    6. if s, err := strconv.ParseFloat(f, 64); err == nil {
    7. fmt.Println(s) // 3.14159265
    8. }
  2. Float 转换为 String

    1. 性能比较差的做法

      1. [root@liqiang.io]# cat test.go
      2. s := fmt.Sprintf("%f", 123.456) // s == "123.456000"
    2. 性能更好的做法

      1. [root@liqiang.io]# cat test.go
      2. strconv.FormatFloat(f, 'g', -1, 32)

去除字符串中非 Ascii 字符

  1. [root@liqiang.io]# cat test.go
  2. func main() {
  3. example := "#GoLangCode!$!"
  4. // Make a Regex to say we only want letters and numbers
  5. reg, err := regexp.Compile("[^a-zA-Z0-9]+")
  6. if err != nil {
  7. log.Fatal(err)
  8. }
  9. processedString := reg.ReplaceAllString(example, "")
  10. fmt.Printf("A string of %s becomes %s \n", example, processedString)
  11. }

uint64 -> String

在 Go 语言中将 uint64 转换成 string 类型:

  1. [root@liqiang.io]# cat uint64.go
  2. var myNumber uint64
  3. myNumber = 18446744073709551615
  4. str := strconv.FormatUint(myNumber, 10)

bytes

bytes 转为 Reader

  1. [root@liqiang.io]# cat test.go
  2. r := bytes.NewReader(byteData)

数字

Float64 的 Nan 和 Inf

注意这段代码:

  1. [root@liqiang.io]# cat nan.go
  2. nan := math.NaN()
  3. pos_inf := math.Inf(1)
  4. neg_inf := math.Inf(-1)
  5. fmt.Println(nan == nan) // false
  6. fmt.Println(pos_inf == pos_inf) // true
  7. fmt.Println(neg_inf == neg_inf) // true
  8. fmt.Println(pos_inf == neg_inf) // false

结构体

结构体指针和非指针的差异

  1. [root@liqiang.io]# cat demo.go
  2. type A struct{
  3. lock sync.Mutex
  4. }
  5. pass(a A) {} // 传值,会拷贝 lock,这是不安全的
  6. for _, a := range []A{}{} // 每次迭代都会拷贝 lock,不安全
  7. for _, a := range []*A{}{} // ok 的,安全

其他

failed to restore the stack

在使用 -race 模式进行单测时,遇到了 data race 的问题,但是,其中给一个位置的报错居然是:

  1. [root@liqiang.io]# cat error.log
  2. 110Write at 0x00c0007311f8 by goroutine 71:
  3. 111 xxxxxxxxxxxxx.keepRegisterLoop()
  4. 112 /builds/xxxxxx:1020 +0x29d
  5. 113Previous read at 0x00c0007311f8 by goroutine 39:
  6. 114 [failed to restore the stack]

这让我蒙了,为什么会这样呢?查了一下资料之后发现这是 Go 的一些局限,后来发现原来是因为我用了反射,所以导致这里无法获取到具体的 stack,解决办法是:

我使用了第一种方式就找到了问题,就是因为使用了反射的缘故。