I need a linked node to hold some different interface types, so I made it with generics, but the generics type any
can't be compared to nil, it shows error as in the comment:
package main
type myInterface interface {
}
type node[T any] struct {
next *node[T]
leaf T
}
func (n *node[T]) GetFirstNodeHasLeaf() *node[T] {
if n.leaf != nil { // <---- error here: cannot compare n.leaf != nil (mismatched types T and untyped nil)
return n
}
if n.next == nil {
return nil
} else {
return n.next.GetFirstNodeHasLeaf()
}
}
func main() {
var n = &node[myInterface]{}
// fill n with lots of nodes
n.GetFirstNodeHasLeaf() // get the first node that has (leaf != nil)
}
I also tried to compare with a default value
var nilT T
if n.leaf != nilT { // <-- same problem
And restrict the node type as
type node[T myInterface] struct {
Same error, how to solve this? Thanks.
CodePudding user response:
Using an interface to instantiate a generic type like node
is probably a conceptual flaw. So let's see the general cases first, and the interface case at the end.
Using comparable
and T
If you want to use equality operators like ==
and !=
with values of type parameter type, the constraint must be comparable
.
type node[T comparable] struct {
next *node[T]
leaf T
}
but then you're not going to test against nil
, you would test against T
's zero value, which, depending on what you instantiate it with, could be something other than nil
.
In that case you would declare a variable of type T
for its zero value:
var zero T
if n.leaf != zero {
return n
}
However interface types don't implement comparable
.
Using any
and *T
As an alternative, you can keep the constraint any
and declare the field leaf
as a pointer to T
. That supports equality operators, because leaf
type isn't a type parameter anymore, it's a pointer:
type node[T any] struct {
next *node[T]
leaf *T
}
func (n *node[T]) GetFirstNodeHasLeaf() *node[T] {
if n.leaf != nil { // ok, leaf is a pointer type
return n
}
...
}
Using any
and T
With the constraint any
, T
doesn't support the equality operators because technically you could instantiate node
with literally any type, including those that aren't comparable.
So as long as T
is constrained by any
and the field isn't a pointer, you can only use reflection to check for the zero value (nil
for pointer types`):
if !reflect.ValueOf(n.leaf).IsZero() {
return n
}
Finally, consider that the above code doesn't work if T
is an interface type. What is tested is the dynamic value boxed in the interface. If T
really must be an interface, test for the zero value with:
// leaf is an interface type
if !reflect.ValueOf(&n.leaf).Elem().IsZero() {
return n
}