Go值比较规则和其中的一些细节

以下文章来源于Go 101 ,作者老貘

在Go中,两个类型确定值是否可以比较取决于这两个值的类型是否相同或者其中的一个是否可以隐式转换为另一个的类型。另外一条规则是不可比较类型的值不能参与比较。此第二条规则的优先级比第一条要高。

Go中有三种主要隐式转换规则:

  1. 如果一个类型T实现了一个接口类型,则类型T的值可以被隐式转换为此接口类型;
  2. 如果两个类型的底层类型相同,并且它们之中至少有一个为非定义类型,则其中任一个类型的值可以隐式转换为另一个类型。
  3. 如果两个元素类型相同的数据通道类型之中至少有一个为双向通道,并且它们之中至少有一个为非定义类型,则其中任一个类型的值可以隐式转换为另一个类型。

比如,下面展示了一些合法和非法的比较的例子:


package main 

func main() { 
  { 
    // A、B和C的底层类型相同。 
    // A表示一个非定义类型。 
    type A = [3]int 
    type B [3]int 
    type C [3]int 
    a := A{} 
    b := B{} 
    c := C{} 

    _ = a == c // ok 
    _ = a == b // ok 
    // 下一行编译不过,因为B和C均为定义类型 
    _ = b == c // error 
  } 

  { 
    var n = 123  // int 
    var t = "Go" // string 
    var i interface{} 

    // int和string都实现了interface{} 
    _ = n == i // ok 
    _ = t == i // ok 
  } 

  { 
    type C1 chan int 
    type C2 chan

一条例外规则为:虽然不可比较类型的值不能参与比较,但是切片、映射和函数值可以和裸nil标识符进行比较已确定这些值是否为它们各自的类型的零值。比如,下面这几个比较都是合法的:


package main 

func main() { 
  var s = []int{1, 2, 3} 
  var m = map[int]bool{1: true, 0: false} 
  var f = func() {} 
  _ = s == nil 
  _ = nil == m 
  _ = f == nil 
} 

当比较两个接口值时:

  • 如果这两个接口的动态类型为同一个不可比较类型,则此比较将在运行时刻产生一个恐慌。
  • 如果这两个接口的动态类型不同,则比较结果肯定为false。

示例:


package main 

func main() { 
  var s = []int{1, 2, 3} 
  var m = map[int]bool{1: true, 0: false} 
  var f = func() {} 

  var a, b, c interface{} = s, m, f 

  println(a == b) // false 
  println(a == c) // false 
  println(c == b) // false 

  // 下面三行都会导致恐慌 
  _ = a == a 
  _ = b == b 
  _ = c == c 

  // 下面三行编译不通过 
  _ = a == s 
  _ = b == m 
  _ = c == f 
} 

接口值可以看作是包裹非接口值盒子。一个什么值都没包裹的接口值为一个nil接口值。一个包裹了一个nil非接口值(比如切片/映射/函数/指针/数据通道类型的值)的接口值不是一个nil接口值。一个nil接口值和一个非nil接口值肯定不相等,因为它们的动态类型不同。例子:


package main 

func main() { 
  var s []int        // nil 
  var m map[int]bool // nil 
  var f func()       // nil 
  var p *int         // nil 
  var c chan bool    // nil 

  var x, y, z interface{} = s, m, f 
  var i interface{} // nil 

  println(i == x) // false 
  println(i == y) // false 
  println(i == z) // false 
  println(i == p) // false 
  println(i == c) // false 
} 
8