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.
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:
Struct values are comparable if all their fields are comparable. Two struct values are equal if their corresponding non-blank fields are equal.
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.
So this is to explain why "v1 == v3" can get a true result. Thanks all.
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.
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 typeT
("x
is assignable toT
") in any of these cases:
x
's type is identical toT
.x
's typeV
andT
have identical underlying types and at least one ofV
orT
is not a defined type.T
is an interface type andx
implementsT
.x
is a bidirectional channel value,T
is a channel type,x
's typeV
andT
have identical element types, and at least one ofV
orT
is not a defined type.x
is the predeclared identifiernil
andT
is a pointer, function, slice, map, channel, or interface type.x
is an untyped constant representable by a value of typeT
.
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 bothfalse
.- 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
andv
are equal if bothreal(u) == real(v)
andimag(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 typeX
and a valuet
of interface typeT
are comparable when values of typeX
are comparable andX
implementsT
. They are equal ift
's dynamic type is identical toX
andt
's dynamic value is equal tox
.- 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 }