Go编程语言Specification说。
如果所有字段都具有可比性,则结构值具有可比性。如果相应的非空白字段相等,则两个struct值相等。
但是,如下面的代码片段,它似乎是变量v1,而v3有不同的类型,为什么它们可以获得真正的输出:
package main
import "fmt"
import "reflect"
type T1 struct { name string }
type T2 struct { name string }
func main() {
v1 := T1 { "foo" }
v2 := T2 { "foo" }
v3 := struct{ name string } {"foo"}
v4 := struct{ name string } {"foo"}
fmt.Println("v1: type=", reflect.TypeOf(v1), "value=", reflect.ValueOf(v1)) // v1: type= main.T1 value= {foo}
fmt.Println("v2: type=", reflect.TypeOf(v2), "value=", reflect.ValueOf(v2)) // v2: type= main.T2 value= {foo}
fmt.Println("v3: type=", reflect.TypeOf(v3), "value=", reflect.ValueOf(v3)) // v3: type= struct { name string } value= {foo}
fmt.Println("v4: type=", reflect.TypeOf(v4), "value=", reflect.ValueOf(v4)) // v4: type= struct { name string } value= {foo}
//fmt.Println(v1 == v2) // compiler error: invalid operation: v1 == v2 (mismatched types T1 and T2)
fmt.Println(v1 == v3) // true, why? their type is different
fmt.Println(v2 == v3) // true, why?
fmt.Println(v3 == v4) // true
}
v1 == v2
因编译错误而失败是合理的,因为它们是不同的类型,但是如何解释v1 == v3
得到true
结果,因为它们也有不同的类型,一个有命名struct type T1
,另一个是匿名结构。
感谢。
感谢@icza,@ John Weldon的解释,我认为这个问题已经解决了,我现在正在更新这个问题。
总之,如果结构符合以下2个规范,则结构具有可比性:
如果所有字段都具有可比性,则结构值具有可比性。如果相应的非空白字段相等,则两个struct值相等。
- 醇>
在任何比较中,第一个操作数必须可分配给第二个操作数的类型,反之亦然。
第一个是结构类型变量特定的;第二个是用于所有类型的变量比较,当然也包括结构类型变量。
在我的示例中,比较变量v1和v3符合这两个规范定义。
所以这是为了解释为什么“v1 == v3”可以得到真实的结果。 谢谢大家。
答案 0 :(得分:5)
如果仔细阅读该规范,可以看到如果相应的非空白字段相等,则两个结构相等。如果类型 name 不同,编译器将失败,但如果其中一个或两个类型是匿名的,那么它们将具有可比性。类型T1
或T2
以及匿名结构实际上是相同的类型,因为它们具有相同的字段。当字段值相同时,它们会比较相同。
查看规范中的type identity可能(或可能不是,ymmv)使其更清晰。
如果两个结构类型具有相同的字段序列,并且相应的字段具有相同的名称,相同的类型和相同的标记,则它们是相同的。来自不同包的未导出字段名称总是不同的。
因此,如果您尝试相同的实验,但通过添加字段标记或通过将类型放在不同的包中来更改类型,您可能会得到您期望的差异。
答案 1 :(得分:2)
您错过的是(相等)比较运算符不要求其操作数具有相同的类型。该要求根据Spec: Comparison operators:
在任何比较中,第一个操作数必须是assignable到第二个操作数的类型,反之亦然。
是的,类型相等会自动给出assignability属性,但还有其他情况:
值
x
可分配给T
类型的变量("x
可分配给T
")在任何这些情况下:
x
的类型与T
相同。x
的类型V
和T
具有相同的underlying types以及V
或{{1}中的至少一个}不是defined类型。T
是一种界面类型,T
implementsx
。T
是双向渠道值,x
是渠道类型,T
类型x
和V
具有相同的元素类型,T
或V
中至少有一个不是定义的类型。T
是预先声明的标识符x
,nil
是指针,函数,切片,地图,通道或接口类型。T
是一种无类型的constant,可以使用x
类型的值表示。
突出显示的规则适用于此处。 T
,v1
和v2
都具有相同的基础类型(v3
),仅struct { name string }
和v1 == v3
比较{ {1}}的类型为defined。因此,它们是可分配的,从那里,适用于您引用的v2 == v3
值的比较规则清楚地解释了为什么结果为v1
:
如果所有字段都具有可比性,则结构值具有可比性。如果相应的非空白字段相等,则两个struct值相等。
没有什么"令人震惊的"关于这个,如果你考虑一下。您没有比较类型,您需要比较这些值。可能不直观的是何时可以使用比较,但规则在可转让性时清楚且干净地列出。
另一个类似的例子(在Go Playground上试试):
struct
我们正在比较true
和var i interface{} = 3
var j int = 3
fmt.Println(i == j) // Comparing interface{} with int: true
类型的值(甚至更多"遥远"你的结构类型),但这是有效的,结果是{{ 1}}并且没有任何令人震惊的事情,相反,我们期待这一点。 interface{}
可分配给int
类型的变量(一切都可分配给true
)。比较它们时,将创建一个包含int
的隐式interface{}
值,并根据此规则比较这两个接口值:
界面值具有可比性。如果两个接口值具有相同的动态类型和相同的动态值,或者两者都具有值
interface{}
,则它们相等。
备注:强>
可分配性是要求,但不是令人满意的条件,具有可比性。根据操作数的类型,Spec: Comparison operators中还列出了必须满足的进一步要求:
等于运算符
interface{}
和j
适用于可比较的操作数。排序运算符nil
,==
,!=
和<
适用于 ordered 的操作数。这些术语和比较结果定义如下:
- 布尔值具有可比性。如果两个布尔值同时为
,则它们相等<=
或两个>
。- 以通常的方式对整数值进行比较和排序。
- 浮点值具有可比性和有序性,如IEEE-754标准所定义。
- 复杂值具有可比性。如果
>=
和true
,则两个复杂值false
和u
相等。- 字符串值具有可比性和排序,按字节顺序排列。
- 指针值具有可比性。如果两个指针值指向同一个变量或两者都具有值
v
,则它们相等。不同zero-size变量的指针可能相等也可能不相等。- 频道值具有可比性。如果两个频道值是通过对make的同一调用创建的,或者两者都具有值
real(u) == real(v)
,则它们相等。- 界面值具有可比性。如果两个接口值具有identical动态类型和相等的动态值,或两者都具有值
imag(u) == imag(v)
,则它们相等。- 当类型
nil
的值具有可比性时,非接口类型nil
的值nil
和接口类型x
的值X
是可比较的t
实施T
。如果X
的动态类型与X
相同且T
的动态值等于t
,则它们相等。- 如果所有字段都具有可比性,则结构值具有可比性。如果相应的非blank字段相等,则两个struct值相等。
- 如果数组元素类型的值具有可比性,则数组值是可比较的。如果相应的元素相等,则两个数组值相等。
如果该类型的值不具有可比性,则对具有相同动态类型的两个接口值进行比较会导致run-time panic。此行为不仅适用于直接接口值比较,还适用于将接口值或结构的数组与接口值字段进行比较。
切片,贴图和函数值无法比较。但是,作为特殊情况,可以将切片,映射或函数值与预先声明的标识符
X
进行比较。指针,通道和接口值与nil的比较也是允许的,并且遵循上面的一般规则。
作为一个简单的例子,规范明确规定切片值不具有可比性:
t
即使x
和nil
具有相同的类型,因此i1, i2 := []int{1, 2}, []int{1, 2}
fmt.Println(i1 == i2)
// invalid operation: i1 == i2 (slice can only be compared to nil)
可分配给i1
(反之亦然),仍然不允许进行比较,因此它们是没有可比性。
另一个不太重要的例子是 因此,例如,此结构的值不可比较(因为它具有切片类型的字段且切片不具有可比性):
i2