Почему байтовый срез не удовлетворяет общему ограничению типа любого среза?

У меня есть следующий код с двумя универсальными функциями. Они оба выполняют простую операцию добавления фрагмента к себе и возврата результата. Однако вызов более простой и понятной функции cat1 приводит к ошибке компиляции, в то время как вызов более неуклюже определенной функции cat2 с ее лишним и явным объявлением элемента среза E работает, как и ожидалось. Почему cat1 недопустимо?

func cat1[S ~[]any](s S) S {
  return append(s, s...)
}

func cat2[S ~[]E, E any](s S) S {
  return append(s, s...)
}

func main() {
  fmt.Println(string(cat2([]byte("hello"))))
}

Ошибка при вызове cat1:

[]byte does not satisfy ~[]any ([]byte missing in ~[]any)

https://go.dev/play/p/IJzCEiPglgA

~[]any в качестве ограничения означает любой тип данных, полученный из фрагмента any. Более длинное ограничение означает, что у вас должен быть тип, производный от фрагмента E, где E может быть чем угодно.
Burak Serdar 23.08.2024 18:00

Кроме того, срезы не являются ковариантными: byte является подтипом any, но []byte не является подтипом []any.

jub0bs 23.08.2024 18:36

Итак, мне нужно включить параметр типа E any, хотя я никогда не объявляю, не использую и не возвращаю переменные типа E в этой функции? Я приму это как ответ, хотя кажется, что компилятор должен понимать, что []byte удовлетворяет []any точно так же, как byte удовлетворяет any. Выглядит педантично.

Todd 23.08.2024 19:17
[]byte не удовлетворяет []any, поскольку это отдельные типы с разным расположением базовой памяти. []any — это не интерфейс, это фрагмент пустых значений интерфейса.
JimB 23.08.2024 19:34
any — это не какой-то тип, это тип any.
Volker 24.08.2024 08:27
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
2
5
75
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

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

Ограничения типа должны быть интерфейсами, однако ключевое слово interface почти всегда опускается как синтаксический сахар.

Поэтому, когда вы используете ~[]any в качестве ограничения типа, вы на самом деле пишете следующее:

interface{ ~[]any }

В этом интерфейсе ~[]any — это термин типа. Какие типы могут удовлетворять этому ограничению? Как и все ограничения:

Аргумент типа T удовлетворяет ограничению типа C, если T является элементом набора типов, определенного C.

Набор типов, определенный вашим ограничением C, он же interface{ ~[]any }, — это литерал типа []any и все типы с []any в качестве базового типа. Например:

type Foo []any

[]byte не включен в набор типов interface{ ~[]any }, точно так же, как вне общего кода вы не можете присвоить []byte[]any:

var a []byte
var b []any
b = a // compilation error

Как упомянул @JimB в комментарии, byte и any имеют разные макеты памяти, поэтому их составные типы не являются взаимоназначаемыми.

Теперь, когда вы используете только any в качестве ограничения типа, все меняется. any — это псевдоним interface{} (пустой интерфейс). В этом случае синтаксический сахар отсутствует. Ограничение — это просто interface{}, который представляет собой интерфейс без методов и типов. Это реализуют все типы, включая byte.

Ваша вторая функция cat2 — единственный правильный способ определить параметр типа S, который может быть любым срезом:

func cat2[S ~[]E, E any](s S) S {
  return append(s, s...)
}

Здесь E ограничено любым типом, а S разрешается как часть того, чем E является.

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