在Golang中,如何将泛型类型的接口与nil进行比较?

aj3*_*423 10 generics go

我需要一个链接节点来保存一些不同的接口类型,所以我用泛型来制作它,但是泛型类型any不能与 nil 进行比较,它显示错误,如注释中所示:

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)
}
Run Code Online (Sandbox Code Playgroud)

我还尝试与默认值进行比较

    var nilT T
    if n.leaf != nilT { // <-- same problem
Run Code Online (Sandbox Code Playgroud)

并将节点类型限制为

type node[T myInterface] struct {
Run Code Online (Sandbox Code Playgroud)

同样的错误,请问如何解决?谢谢。

bla*_*een 10

使用接口来实例化泛型类型node可能是一个概念缺陷。因此,让我们先看一般情况,最后看接口情况。

使用comparableT

如果要对类型参数 type 的值使用等号运算符(例如==!=),则约束必须是comparable

type node[T comparable] struct {
    next *node[T]
    leaf T
}
Run Code Online (Sandbox Code Playgroud)

但是,您不会针对 进行测试nil,而是针对 的零值进行测试T,根据您实例化它的内容,该零值可能是除 之外的其他值nil

T在这种情况下,您可以为其零值声明一个类型变量:

var zero T
if n.leaf != zero {
    return n
}
Run Code Online (Sandbox Code Playgroud)

但是接口类型不实现comparable.

使用any*T

作为替代方案,您可以保留约束any并将该字段声明leaf为指向 的指针T。它支持相等运算符,因为leaf类型不再是类型参数,而是指针:

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
    }
...
}
Run Code Online (Sandbox Code Playgroud)

使用anyT

带约束any,T不支持等式运算符;您可以node使用任何类型进行实例化,包括那些不可比较的类型。

只要该字段不是指针,您就只能使用反射来检查零值(nil对于指针类型):

if !reflect.ValueOf(n.leaf).IsZero() {
    return n
}
Run Code Online (Sandbox Code Playgroud)

T最后,请考虑如果是接口类型,则上述代码不起作用。测试的是界面中装箱的动态值。如果T确实必须是一个接口,请使用以下命令测试零值:

// leaf is an interface type
if !reflect.ValueOf(&n.leaf).Elem().IsZero() {
    return n
}
Run Code Online (Sandbox Code Playgroud)