duanmao9918
duanmao9918
2017-10-11 03:47

golang为什么命名和未命名结构比较的结果相同

已采纳

The Go Programming Language Specification said.

Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

But as following code snippet, it seems variable v1, and v3 has different type, why they can get a true output:

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
}

It's reasonable that v1 == v2 fails with compile error because they are different type, however how to explain the v1 == v3 get a true result, since they also have different types, one with named struct type T1, and the other with anonymous struct. Thanks.

Update Question based on feedback

Thanks @icza, @John Weldon for your explanation, I think this issue is resolved, I am now updating the question.

In summary, a struct is comparable if it meet following 2 specs:

  1. Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

  2. In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

The 1st one is for struct type variable specific; and the 2nd one is for all types variable comparison, struct type variable is covered of course.

In my sample, the comparison variable v1 and v3 meet these two specs definition.

  1. All fields are comparable; in fact, the first spec define the struct rule, it focus on fields, but not struct itself, so whatever a named struct or anonymous struct, they are same rule.
  2. Variable v1 and v3 is assignable. (according to the rule: x's type V and T have identical underlying types and at least one of V or T is not a defined type)

So this is to explain why "v1 == v3" can get a true result. Thanks all.

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

2条回答

  • duanhao7786 duanhao7786 4年前

    If you read that spec carefully, you can see that if the corresponding non-blank fields are equal then the two structs are equal. If the type name is different the compiler will fail, but if one or both of the types are anonymous then they'll be comparable. The types T1 or T2, and the anonymous structs are effectively the same type because they have the same fields. When the field values are the same then they compare as the same.

    Looking at type identity in the spec may (or may not, ymmv) make it clearer.

    Two struct types are identical if they have the same sequence of fields, and if corresponding fields have the same names, and identical types, and identical tags. Non-exported field names from different packages are always different.

    So, if you try the same experiment but change the types by adding field tags, or by putting the types in different packages, you may get the differences you expect.

    点赞 评论 复制链接分享
  • doushi7761 doushi7761 4年前

    What you miss is that the (equality) comparison operator does not require its operands to be of the same type. The requirement is according to the Spec: Comparison operators:

    In any comparison, the first operand must be assignable to the type of the second operand, or vice versa.

    Yes, type equality automatically gives the assignability attribute, but there are other cases:

    A value x is assignable to a variable of type T ("x is assignable to T") in any of these cases:

    • x's type is identical to T.
    • x's type V and T have identical underlying types and at least one of V or T is not a defined type.
    • T is an interface type and x implements T.
    • x is a bidirectional channel value, T is a channel type, x's type V and T have identical element types, and at least one of V or T is not a defined type.
    • x is the predeclared identifier nil and T is a pointer, function, slice, map, channel, or interface type.
    • x is an untyped constant representable by a value of type T.

    The highlighted rule applies here. v1, v2 and v3 all have the same underlying type (which is struct { name string }), and in the v1 == v3 and v2 == v3 comparisons only v1's type is defined. So they are assignable, and from there, the comparison rule that applies to struct values you quoted clearly explains why the result is true:

    Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.

    There's nothing "shocking" about this if you think about it. You're not comparing the types, you're comparing the values. What may not be intuitive is when the comparison may be used, but the rules are listed clearly and cleanly at assignability.

    Another similar example (try it on the Go Playground):

    var i interface{} = 3
    var j int = 3
    fmt.Println(i == j) // Comparing interface{} with int: true
    

    We're comparing values of interface{} and int types (even more "distant" that your struct types), yet this is valid and the result is true and there's nothing shocking about that either, on the contrary, we'd expect that. int is assignable to a variable of type interface{} (everything is assignable to interface{}). When comparing them, an implicit interface{} value will be created that wraps j, and those 2 interface values will be compared according to this rule:

    Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.

    Notes:

    Assignability is a requirement but not a satisfactory condition to comparability. Further requirements are outlined in the Spec: Comparison operators that also must be met, based on the types of the operands:

    The equality operators == and != apply to operands that are comparable. The ordering operators <, <=, >, and >= apply to operands that are ordered. These terms and the result of the comparisons are defined as follows:

    • Boolean values are comparable. Two boolean values are equal if they are either both true or both false.
    • Integer values are comparable and ordered, in the usual way.
    • Floating-point values are comparable and ordered, as defined by the IEEE-754 standard.
    • Complex values are comparable. Two complex values u and v are equal if both real(u) == real(v) and imag(u) == imag(v).
    • String values are comparable and ordered, lexically byte-wise.
    • Pointer values are comparable. Two pointer values are equal if they point to the same variable or if both have value nil. Pointers to distinct zero-size variables may or may not be equal.
    • Channel values are comparable. Two channel values are equal if they were created by the same call to make or if both have value nil.
    • Interface values are comparable. Two interface values are equal if they have identical dynamic types and equal dynamic values or if both have value nil.
    • A value x of non-interface type X and a value t of interface type T are comparable when values of type X are comparable and X implements T. They are equal if t's dynamic type is identical to X and t's dynamic value is equal to x.
    • Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.
    • Array values are comparable if values of the array element type are comparable. Two array values are equal if their corresponding elements are equal.

    A comparison of two interface values with identical dynamic types causes a run-time panic if values of that type are not comparable. This behavior applies not only to direct interface value comparisons but also when comparing arrays of interface values or structs with interface-valued fields.

    Slice, map, and function values are not comparable. However, as a special case, a slice, map, or function value may be compared to the predeclared identifier nil. Comparison of pointer, channel, and interface values to nil is also allowed and follows from the general rules above.

    As a trivial example the spec explicitly states slice values are not comparable:

    i1, i2 := []int{1, 2}, []int{1, 2}
    fmt.Println(i1 == i2)
    // invalid operation: i1 == i2 (slice can only be compared to nil)
    

    Even though i1 and i2 have identical types, and therefore i1 is assignable to i2 (and vice versa), comparison is still not allowed and therefore they are not comparable.

    Another less trivial example is that structs may be comparable if all their fields are comparable. So for example values of this struct are not comparable (because it has a field of slice type and slices are not comparable):

    type Foo struct { Names []string }
    
    点赞 评论 复制链接分享

相关推荐