2

This piece of code looks quite counter-intuitive to me:

var first *byte
var second interface{}

fmt.Println(first, first == nil)       // <nil> true
fmt.Println(second, second == nil)     // <nil> true
fmt.Println(first == second)           // false

As far as I understand, the case is that the first variable is a pointer to an empty variable of type byte, whereas the second is an empty interface. So, as the variables are not the same type, they are not considered equal.

But if they are not equal to each other, how can they be equal to some third value? Is it common situation in programming languages when Transitive Law is not held?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
John Snow
  • 339
  • 4
  • 17
  • 1
    `nil` is a special value, don't build your logic on it. Also see [Hiding nil values, understanding why golang fails here](https://stackoverflow.com/questions/29138591/hiding-nil-values-understanding-why-golang-fails-here/29138676#29138676). – icza May 12 '18 at 12:23
  • Possible duplicate of [Hiding nil values, understanding why golang fails here](https://stackoverflow.com/questions/29138591/hiding-nil-values-understanding-why-golang-fails-here) – Michael Hampton May 12 '18 at 17:38
  • 1
    See also [Why does Go have typed nil?](https://stackoverflow.com/q/19761393/1068283) – Michael Hampton May 12 '18 at 17:38

3 Answers3

3

The identifier nil represents the zero value for channel, pointer, interface, function, map and slice types.

The zero value for a *byte is not equal to the zero value for an interface{}.

Charlie Tumahai
  • 113,709
  • 12
  • 249
  • 242
1

Nil does represent a zero value, but each value in go also has a type.

I have also seen something similar to this in testing when methods return a custom error type as err, and then check to see if the errors are equal which fails because Go intrinsically believes they are different because you have not type asserted it to the custom error type.

brun
  • 133
  • 9
1

Here's a more interesting example of intransitivity of Go's == operator. It doesn't rely on nil; for types like functions and maps, x==nil is best thought of as an special operator, since values of those types aren't comparable to each other.

// "hello" != x == s == "hello" func main() { type S string var s S = "hello" var x interface{} = s fmt.Println(s == "hello") // s == "hello" fmt.Println(x == s) // x == s fmt.Println(x == "hello") // x != "hello" }

One way to explain this anomaly is that there are really two different equality operators at work here. The first comparison has type S, but the second and third have type interface{}. Once the types are erased, the interface conversions become explicit and each "==" is replaced by the appropriate comparison operator for its type. Those comparison operators, individually, are true equivalence relations, which is to say they are transitive.

  • Thank you for the great example. Could you please clarify why the third statement is `x == "hello"` is false? According to the specification `if one operand is an untyped constant and the other operand is not, the constant is converted to the type of the other operand.` If we convert string constant `hello` to `x`, should not they be equal? – John Snow Jan 06 '19 at 11:31