У меня есть следующий код с двумя универсальными функциями. Они оба выполняют простую операцию добавления фрагмента к себе и возврата результата. Однако вызов более простой и понятной функции 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)
Кроме того, срезы не являются ковариантными: byte
является подтипом any
, но []byte
не является подтипом []any
.
Итак, мне нужно включить параметр типа E any
, хотя я никогда не объявляю, не использую и не возвращаю переменные типа E
в этой функции? Я приму это как ответ, хотя кажется, что компилятор должен понимать, что []byte
удовлетворяет []any
точно так же, как byte
удовлетворяет any
. Выглядит педантично.
[]byte
не удовлетворяет []any
, поскольку это отдельные типы с разным расположением базовой памяти. []any
— это не интерфейс, это фрагмент пустых значений интерфейса.
any
— это не какой-то тип, это тип any
.
Ограничения типа должны быть интерфейсами, однако ключевое слово 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
является.
~[]any
в качестве ограничения означает любой тип данных, полученный из фрагментаany
. Более длинное ограничение означает, что у вас должен быть тип, производный от фрагментаE
, гдеE
может быть чем угодно.