This is just bad semantics. You’d expect that coercing a type to its supertype preserves all properties shared with the supertype including nil. For “implementation reasons” this isn’t happening.
Hm, maybe I’m misunderstanding then, but if isNil is a property of all types in Go and I can silently convert a value to a substantiation of an interface then I’d expect isNil to be preserved. That silent coercion is the subtyping relationship.
Now also clearly this doesn’t quite work from a memory perspective since coercing to an interface means we need to reference the methods somehow or another, but that’s why I think this is a convenient-but-wrong kind of behavior.
This is just bad semantics. You’d expect that coercing a type to its supertype preserves all properties shared with the supertype including nil. For “implementation reasons” this isn’t happening.
Not so. Printing all the details…
fmt.Printf(”%#v %#v %#v %v”, t, t2, thing, thing.P == nil)
Provides…
&main.T{}
(*main.T)(nil)
&main.Thing{P:(*main.T)(nil)}
false
Clearly the interface P in the third position is not nil. Rather it is implemented by a nil *T
It’s fair to dislike the Go interface mechanism, but it is not the case that this is type / supertype.
Hm, maybe I’m misunderstanding then, but if
isNil
is a property of all types in Go and I can silently convert a value to a substantiation of an interface then I’d expectisNil
to be preserved. That silent coercion is the subtyping relationship.Now also clearly this doesn’t quite work from a memory perspective since coercing to an interface means we need to reference the methods somehow or another, but that’s why I think this is a convenient-but-wrong kind of behavior.
Think of Go interfaces as a pair of a type tag and a value of that type. In the original code the call…
factory(t2)
Go implicitly creates a P interface instance as the pair (*T, nil)
And the original predicate asks…
Given a pair (*T, nil) and the constant nil are they == ?
And the answer is false.
In another comment on this page I modified the example to…
var pnil P = (*T)(nil); fmt.Println(thing.P == pnil)
In this case Go implicitly creates a P interface instance as the pair (*T, nil) and the variable pnil is assigned that value.
In this example the predicate asks…
Given a pair (*T, nil) and another pair (*T, nil) are they == ?
In this case the answer is true.
The following prints true.
var pnil P = (*T)(nil); fmt.Println(thing.P == pnil)
However the real problem is this smells bad – avoid having to inspect interface implementations.
The simple solution is having lint warn of comparing an interface via ==
[Comment removed by author]
Agreed, I think the false positives would outweigh the offenses. This only adds to my inclination that typed nil isn’t a great problem for Go 2.