1、赋值规则
赋值可以看作是隐式类型转换
赋值语句中的目标值必须为一个可寻址的值、一个映射元素表达式或者一个空标识符。
在一个赋值中,源值被复制给了目标值。精确地说,源值的直接部分被复制给了目标值。
2、值比较规则
在任何比较中,第一个比较值必须能被赋值给第二个比较值的类型,或者反之。
所以,值比较规则和赋值规则非常相似。 换句话说,两个值是否可以比较取决于其中一个值是否可以隐式转换为另一个值的类型。 但是有一个例外:
如果两个值中有一个为接口值,而另一个为非接口值并且它的类型为一个不可比较类型,则这两个值不可比较,即使此非接口值可以被隐式转换为接口值的类型(即此不可比较类型实现了接口值的类型)。
注意,尽管切片/映射/函数类型为不可比较类型,但是它们的值可以和类型不确定的预声明nil标识符比较。
上述规则并未覆盖所有的情况。如果两个值均为类型不确定值,它们可以比较吗?这种情况的规则比较简单:
两个类型不确定的布尔值可以相互比较。
两个类型不确定的数字值可以相互比较。
两个类型不确定的字符串值可以相互比较。
两个类型不确定的数字值的比较结果服从直觉。
注意,两个类型不确定的nil值不能相互比较。
var s []int
var m map[int]int
var f func()()
var t struct {x []int}
var a [5]map[int]int
func main() {
// 这些比较编译不通过。
_ = s == s
_ = m == m
_ = f == f
_ = t == t
_ = a == a
_ = nil == nil
_ = s == interface{}(nil)
_ = m == interface{}(nil)
_ = f == interface{}(nil)
// 这些比较编译都没问题。
_ = s == nil
_ = m == nil
_ = f == nil
_ = 123 == interface{}(nil)
_ = true == interface{}(nil)
_ = "abc" == interface{}(nil)
}
3、两个值是如何进行比较的?
假设两个值可以相互比较,并且它们的类型同为T。 (如果它们的类型不同,则其中一个可以转换为另一个的类型。这里我们不考虑两者均为类型不确定值的情形。)
(1)如果T是一个布尔类型,则这两个值只有在它们同为true或者false的时候比较结果才为true。
(2)如果T是一个整数类型,则这两个值只有在它们在内存中的表示完全一致的情况下比较结果才为true。
(3)如果T是一个浮点数类型, 则这两个值只要满足下面任何一种情况,它们的比较结果就为true:
它们都为+Inf;
它们都为-Inf;
它们都为-0.0或者都为+0.0。
它们都不是NaN并且它们在内存中的表示完全一致。
(4)如果T是一个复数类型,则这两个值只有在它们的实部和虚部均做为浮点数进行进行比较的结果都为true的情况下比较结果才为true。
(5)如果T是一个指针类型(类型安全或者非类型安全),则这两个值只有在它们所表示的地址值相等或者它们都为nil的情况下比较结果才为true。
(6)如果T是一个通道类型,则这两个值只有在它们引用着相同的底层内部通道或者它们都为nil时比较结果才为true。
(7)如果T是一个结构体类型,则它们的相应字段将逐对进行比较。只要有一对字段不相等,这两个结构体值就不相等。
(8)如果T是一个数组类型,则它们的相应元素将逐对进行比较。只要有一对元素不相等,这两个结构体值就不相等。
(9)如果T是一个接口类型,请参阅两个接口值是如何进行比较的。(两个接口值的比较结果只有在下面两种任一情况下才为true)
这两个接口值都为nil接口值
这两个接口值的动态类型相同、动态类型为可比较类型、并且动态值相等。
(10)如果T是一个字符串类型,请参阅两个字符串值是如何进行比较的。
请注意,动态类型均为同一个不可比较类型的两个接口值的比较将产生一个恐慌。比如下面的例子: