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

huangapple go评论75阅读模式
英文:

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

问题

我需要一个链接节点来保存一些不同的接口类型,所以我使用了泛型来创建它,但是泛型类型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)
}

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

	var nilT T
	if n.leaf != nilT { // <-- same problem

并且将节点类型限制为:

type node[T myInterface] struct {

仍然出现相同的错误,请问如何解决这个问题?谢谢。

英文:

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.

答案1

得分: 6

使用接口来实例化泛型类型node可能是一个概念上的缺陷。所以我们先看一下一般情况,最后再看接口的情况。

使用comparableT

如果你想要在类型参数类型的值上使用等号操作符==!=,约束条件必须是comparable

type node[T comparable] struct {
    next *node[T]
    leaf T
}

但是你不会对nil进行测试,你会对T的零值进行测试,这取决于你实例化它时使用的值,可能不是nil

在这种情况下,你可以声明一个类型为T的变量来表示它的零值:

var zero T
if n.leaf != zero {
    return n
}

然而,接口类型不实现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
    }
...
}

使用anyT

在约束条件为any的情况下,T不支持等号操作符;你可以使用任意类型来实例化node,包括那些不可比较的类型。

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

if !reflect.ValueOf(n.leaf).IsZero() {
    return n
}

最后,请注意上述代码在T是接口类型时不起作用。被测试的是接口中封装的动态值。如果T必须是一个接口,可以使用以下代码来测试零值:

// leaf是一个接口类型
if !reflect.ValueOf(&n.leaf).Elem().IsZero() {
    return n
}
英文:

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
}

huangapple
  • 本文由 发表于 2022年10月9日 04:14:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/74000242.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定