Существует ли ограничение универсального типа Go, которое фиксирует возможность использования типа в качестве ключа на карте?

В приведенном ниже коде я определяю общий связанный список. Go1.18 с удовольствием использует экземпляр списка в качестве ключа к карте. Однако последняя строка, если она не закомментирована, не компилируется; Я получаю сообщение об ошибке:

Cons[int] does not implement comparable

Есть ли более слабое ограничение типа, которое я могу использовать, чтобы выбрать те типы, которые можно использовать в качестве ключей, или это предназначено, или это ошибка компилятора?

package main

import "fmt"

type List[X any] interface {
    isList()
}

type Cons[X any] struct {
    Data X
    Next List[X]
}

func (Cons[X]) isList() {}

type Nil[X any] struct{}

func (Nil[X]) isList() {}

func id[X comparable](x X) X { return x }

func main() {
    x := Cons[int]{5, Nil[int]{}}
    m := map[List[int]]string{}
    m[x] = "Hi"        // succeeds
    fmt.Println(m[x])  // prints "Hi"
    // fmt.Println(id(x)) // fails
}
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
2
0
134
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Предварительно объявленное ограничение comparable является правильным универсальным ограничением для ключей карты, поскольку оно реализовано типами, поддерживающими == и != (условие использования в качестве ключей карты), но не интерфейсы 1.

Это упоминается здесь: https://go.dev/ref/spec#Type_constraints

The predeclared interface type comparable denotes the set of all non-interface types that are comparable. Specifically, a type T implements comparable if:

  • T is not an interface type and T supports the operations == and !=
  • T is an interface type and each type in T's type set implements comparable

Это важный нюанс, потому что базовые типы интерфейсов обычно поддерживают операторы равенства — сравниваются их динамические типы/значения.

Следовательно, ваш интерфейс List[X] можно использовать как ключ карты напрямую, как в map[List[int]]string{}, но он не реализует comparable, потому что имеет бесконечный набор типов (у него нет термов, поэтому его реализует любой тип). И Cons тоже не реализует, потому что имеет поле типа List[X]. Для этого нет более слабого ограничения.

Учтите, что ограничения, которые встраивают comparable, также действительны для ключей карты, поэтому, если вам действительно нужен метод isList() в теле функции, вы можете определить ограничение, подобное этому, и реализовать его в ваших структурах списков, которые являются ключами карты, вместо объявления поля интерфейса:

// may use this as a constraint
type List interface {
    comparable
    isList() bool
}

1: the quote from the specs hints there are interface types that implement comparable, but it's effectively not possible to instantiate comparable with any interface at all: interfaces with only methods have an infinite type set, and interfaces with type terms can't be used anywhere except as constraints.

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