Go 语言的几大坑

nil slice 与 empty slice 的区别

func main() {
  var s1 []string
  s2 := []string{}

  fmt.Printf("s1 == nil: %v\n", s1 == nil)  // s1 == nil: true
  fmt.Printf("s2 == nil: %v\n", s2 == nil)  // s2 == nil: false
}

阅读 Golang: Nil vs Empty Slice (零值切片和空切片的坑) (链接备份)。

interface 的空值判断问题

func main(){
  var a interface{} = nil
  var b interface{} = (*int)(nil)

  fmt.Println(a == nil)  // true
  fmt.Println(b == nil)  // false
}

阅读 Go “一个包含nil指针的接口不是nil接口”踩坑 (链接备份)。

append 一定返回新的 slice,但内部数组不一定是新的

得根据是否超出 cap 来判断。

func main(){
  arr := []int{0, 1, 2}
  func(v []int) {
    v[0] = 100       // modify origin array
    v = append(v, 4) // new array allocated, and new slice variable
    v[0] = 50        // not modify origin array
  }(arr)
  fmt.Println(arr)   // [100 1 2]
}
func main() {
  var arr = make([]int, 0, 5)
  arr = append(arr, 0, 1, 2)

  func(v []int) {
    v[0] = 100       // modify origin array
    v = append(v, 4) // no new array allocated, but new slice variable
    v[0] = 50        // modify origin array
  }(arr)

  fmt.Println(arr)   // [50 1 2]
}

数组是值传递

func main() {
  x := [3]int{1, 2, 3}

  func(arr [3]int) {
    arr[0] = 7
    fmt.Println(arr) // [7 2 3]
  }(x)

  fmt.Println(x) // [1 2 3]
}

slice 循环问题

func main() {
  var out []*int
  for i := 0; i < 3; i++ {
    out = append(out, &i)
  }
  fmt.Println("value:", *out[0], *out[1], *out[2]) // value: 3 3 3
  fmt.Println("pointer:", out[0], out[1], out[2])  // pointer: 0x14000018178 0x14000018178 0x14000018178
}
func main() {
  var out []*int
  for _, i := range []int{1, 2, 3} {
    out = append(out, &i)
  }
  fmt.Println("value:", *out[0], *out[1], *out[2]) // value: 3 3 3
  fmt.Println("pointer:", out[0], out[1], out[2])  // pointer: 0x14000018178 0x14000018178 0x14000018178
}

因为 i 始终是同一个变量,地址没变。

默认零值

当声明变量时未初始化,默认会根据类型赋予零值。

  • 数值类型(包括整数、浮点数和复数):0
  • 布尔类型:false
  • 字符串类型:"" (空字符串)
  • 指针类型:nil
  • 接口类型:nil
  • 函数类型:nil
  • 切片类型:nil
  • 映射类型:nil
  • 通道类型:nil
  • 结构体类型:其所有成员变量的默认零值

当 UnmarshalJSON 时,比如结构体定义了布尔类型的字段,无法区分是传了 false 还是没有传值,因为默认值就是 false。 解决方案是定义 type ConvertibleBoolean bool 以及 func (bit *ConvertibleBoolean) UnmarshalJSON(data []byte)。 参考 https://stackoverflow.com/a/37214476/4622308