In Golang, how to compare interface as generics type to nil?
Asked Answered
D

3

10

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.

Davidadavidde answered 8/10, 2022 at 20:14 Comment(0)
I
12

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; you could instantiate node with literally any type, including those that aren't comparable.

As long as 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
}
Inoue answered 8/10, 2022 at 20:37 Comment(0)
F
0

You can use interface with generics to declare that your T is pointer.

type PointerType[V any] interface {
    *V
}
type node[T PointerType[V], V any] struct {
    next *node[T, V]
    leaf T
}

func (n *node[T, V]) GetFirstNodeHasLeaf() *node[T, V] {
    if n.leaf != nil { 
        return n
    }
    return nil
}
Fritzie answered 27/6 at 13:41 Comment(0)
H
-1

Please try code:

func OrValue[T comparable](v T, or T) (t T) {
if v == t {
    return or
}
return v

}

func TestOrValue(t *testing.T) {
v := types.OrValue("str", "1")
t.Log(v)

}

Hungary answered 9/6 at 19:24 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Darendaresay

© 2022 - 2024 — McMap. All rights reserved.