golang为什么命名和非命名的struct比较具有相同的结果

时间:2017-10-11 03:47:54

标签: go struct types comparison

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个规范,则结构具有可比性:

  
      
  1. 如果所有字段都具有可比性,则结构值具有可比性。如果相应的非空白字段相等,则两个struct值相等。

  2.   
  3. 在任何比较中,第一个操作数必须可分配给第二个操作数的类型,反之亦然。

  4.   

第一个是结构类型变量特定的;第二个是用于所有类型的变量比较,当然也包括结构类型变量。

在我的示例中,比较变量v1和v3符合这两个规范定义。

  1. 所有领域都具有可比性;事实上,第一个规范定义了结构规则,它专注于字段,但不是结构本身,所以无论命名结构还是匿名结构,它们都是相同的规则。
  2. 变量v1和v3是可分配的。 (根据规则:x的类型V和T具有相同的基础类型,并且V或T中的至少一个不是定义的类型)
  3. 所以这是为了解释为什么“v1 == v3”可以得到真实的结果。 谢谢大家。

2 个答案:

答案 0 :(得分:5)

如果仔细阅读该规范,可以看到如果相应的非空白字段相等,则两个结构相等。如果类型 name 不同,编译器将失败,但如果其中一个或两个类型是匿名的,那么它们将具有可比性。类型T1T2以及匿名结构实际上是相同的类型,因为它们具有相同的字段。当字段值相同时,它们会比较相同。

查看规范中的type identity可能(或可能不是,ymmv)使其更清晰。

  

如果两个结构类型具有相同的字段序列,并且相应的字段具有相同的名称,相同的类型和相同的标记,则它们是相同的。来自不同包的未导出字段名称总是不同的。

因此,如果您尝试相同的实验,但通过添加字段标记或通过将类型放在不同的包中来更改类型,您可能会得到您期望的差异。

答案 1 :(得分:2)

您错过的是(相等)比较运算符不要求其操作数具有相同的类型。该要求根据Spec: Comparison operators:

  

在任何比较中,第一个操作数必须是assignable到第二个操作数的类型,反之亦然。

是的,类型相等会自动给出assignability属性,但还有其他情况:

  

x可分配给T类型的变量(" x可分配给T")在任何这些情况下:

     
      
  • x的类型与T相同。
  •   
  • x的类型VT具有相同的underlying types以及V或{{1}中的至少一个}不是defined类型。
  •   
  • T是一种界面类型,T implements x
  •   
  • T是双向渠道值,x是渠道类型,T类型xV具有相同的元素类型,TV中至少有一个不是定义的类型。
  •   
  • T是预先声明的标识符xnil是指针,函数,切片,地图,通道或接口类型。
  •   
  • T是一种无类型的constant,可以使用x类型的值表示。
  •   

突出显示的规则适用于此处。 Tv1v2都具有相同的基础类型(v3),仅struct { name string }v1 == v3比较{ {1}}的类型为defined。因此,它们是可分配的,从那里,适用于您引用的v2 == v3值的比较规则清楚地解释了为什么结果为v1

  

如果所有字段都具有可比性,则结构值具有可比性。如果相应的非空白字段相等,则两个struct值相等。

没有什么"令人震惊的"关于这个,如果你考虑一下。您没有比较类型,您需要比较这些值。可能不直观的是何时可以使用比较,但规则在可转让性时清楚且干净地列出。

另一个类似的例子(在Go Playground上试试):

struct

我们正在比较truevar 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,则两个复杂值falseu相等。
  •   
  • 字符串值具有可比性和排序,按字节顺序排列。
  •   
  • 指针值具有可比性。如果两个指针值指向同一个变量或两者都具有值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

即使xnil具有相同的类型,因此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