Какой тип имеет результат append в обобщённой функции, если параметр типа ограничен срезами через ~?
Результат append имеет тот же тип параметра, что и переданный срез, то есть T, а не его базовый тип []E. Поэтому для именованного типа среза сохраняется его имя и связанный с ним методный набор.
Обобщения в Go позволяют писать алгоритмы для целого семейства типов, сохраняя статическую проверку типов. Ограничение с ~ нужно, когда функция должна принимать не только сам базовый тип среза, но и именованные типы с тем же базовым представлением.
Рассмотрим именованный тип среза, например IDs, базовым типом которого является []int. Если обобщённая функция вернёт обычный []int, вызывающий код потеряет статический тип IDs, а вместе с ним — его методы и совместимость с API, ожидающим именно IDs.
Важно отличать тип параметра от его базового типа. Запись ~[]E описывает множество типов, чьи базовые типы имеют форму []E, но не превращает каждый такой тип в обычный []E.
Встроенная функция append возвращает значение того же типа среза, что и её первый аргумент. Поэтому при первом аргументе типа S результат имеет тип S, даже если ограничение параметра записано через базовый тип.
Результат имеет тип main.IDs, а не []int. Если append не находит свободной ёмкости, он может выделить новый массив и скопировать элементы, но это не меняет статический тип результата.
Ограничение ~[]E также принимает определённые пользователем типы срезов. Ограничение без ~, например точное []int, допускает только сам указанный тип и не расширяет множество до именованных типов с тем же базовым типом.
Есть важное ограничение: методы конкретного типа S не становятся автоматически доступными внутри обобщённой функции только потому, что S удовлетворяет ~[]E. В теле доступны операции, разрешённые самим ограничением; дополнительные методы нужно явно указать в интерфейсном ограничении.
В библиотеке есть именованный тип IDs с методом валидации, а несколько функций должны добавлять элементы, не превращая значение в []int. Вариант с обычной функцией для []int требует явных преобразований и теряет тип IDs; вариант с any сохраняет универсальность, но переносит проверки на время выполнения.
Выбранная обобщённая функция с ограничением S ~[]E: она проверяет допустимость операции на этапе компиляции и возвращает исходный тип среза. В результате вызывающий код сохраняет методы IDs, а реализация остаётся общей для всех подходящих именованных типов.
~[]E именованные типы срезов?Да. Если определённый пользователем тип имеет базовый тип []E, он входит в множество типов ограничения. Например, type IDs []int удовлетворяет ограничению ~[]int, хотя IDs и []int остаются разными типами.
Нет. Перевыделение меняет только underlying-массив и расположение элементов. Тип результата по-прежнему совпадает с типом первого аргумента. При этом могут измениться правила совместного использования массива: ранее существовавшие срезы могут продолжить ссылаться на старый массив.
Нет. Обобщённый код использует методный набор ограничения, а не полный методный набор каждого возможного типа. Если функция должна вызывать метод, его нужно объявить в интерфейсном ограничении; одного ~[]E для этого недостаточно.