В Golang, как сравнить интерфейс как универсальный тип с нулевым?

Мне нужен связанный узел для хранения нескольких разных типов интерфейса, поэтому я сделал его с помощью дженериков, но тип дженериков 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 {

Такая же ошибка, как решить? Спасибо.

Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
4
0
422
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Использование интерфейса для создания универсального типа, такого как node, вероятно, является концептуальным недостатком. Итак, давайте сначала рассмотрим общие случаи, а в конце — случай интерфейса.

Использование comparable и T

Если вы хотите использовать операторы равенства, такие как == и !=, со значениями типа параметра типа, ограничение должно быть 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 больше не параметр типа, а указатель:

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
    }
...
}

Использование any и T

С ограничением anyT не поддерживает операторы равенства; вы можете создать экземпляр node буквально с любым типом, включая те, которые несопоставимы.

Пока поле не является указателем, вы можете использовать отражение только для проверки нулевого значения (nil для типов указателей):

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

Наконец, учтите, что приведенный выше код не работает, если T является типом интерфейса. Тестируется динамическое значение в интерфейсе. Если T действительно должен быть интерфейсом, проверьте нулевое значение с помощью:

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

Другие вопросы по теме