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

Вызов обобщённой функции не получает аргументов, а её параметр типа встречается только в возвращаемом типе. Сможет ли Go вывести этот параметр из типа переменной, куда записывается результат?

Проходите собеседования с ИИ помощником Hintsage

Краткий ответ

Нет. Go не выводит параметр типа из ожидаемого типа результата. Если параметр типа встречается только в возвращаемом значении и не определяется аргументами функции, его нужно указать явно.

Исторический контекст

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

Такой подход сохраняет простые и предсказуемые правила компиляции. Компилятор не пытается выводить типы из общего контекста выражения, включая тип переменной, которой присваивается результат.

Постановка проблемы

Рассмотрим обобщённую функцию, которая не принимает аргументов, но возвращает значение типа T. В месте вызова может быть очевидно, что результат должен иметь тип int, однако этого недостаточно для автоматического выбора T.

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

Подробное решение

Вывод параметров типа в Go выполняется по информации, доступной из аргументов функции и соответствующих ограничений. Если T встречается только в результате, у компилятора нет аргумента вызова, из которого можно было бы вывести T.

Ожидаемый тип присваивания не используется как источник вывода. Поэтому даже объявление переменной типа int не заставляет компилятор выбрать T = int.

package main func Zero[T any]() T { var z T return z } func main() { a := Zero[int]() var b int = Zero[int]() _ = a _ = b }

В обоих вызовах int задан явно. Вызов без типа, например Zero(), не компилируется: параметр T невозможно вывести.

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

Ситуация из практики

Допустим, библиотека предоставляет обобщённую функцию создания нулевого значения типа. Разработчик пишет вызов без явного типа, рассчитывая, что присваивание переменной int подскажет компилятору нужный параметр.

Вариант с неявным выводом выглядит короче, но не работает: контекст присваивания не участвует в выводе. Можно было бы изменить API и передавать фиктивный аргумент типа T, однако это ухудшит читаемость и добавит ненужные данные.

Предпочтительное решение — явно указать тип при вызове. Оно немного увеличивает запись, зато делает источник типа очевидным, не требует искусственных аргументов и не зависит от контекста использования. В результате код компилируется предсказуемо и проще читается при сопровождении.

Что кандидаты часто упускают

1. Если функция возвращает T, разве присваивание переменной типа int не должно вывести T?

Нет. Go не использует ожидаемый тип выражения для вывода параметров типа. Тип переменной проверяется уже после определения типа самого вызова, поэтому он не может выбрать T задним числом.

2. Может ли ограничение единственным образом определить параметр типа?

Обычно нет. Ограничение задаёт множество допустимых типов, а не обязательно один конкретный тип. Например, ограничение any допускает практически любой тип, поэтому без аргумента оно не даёт информации для выбора конкретного T.

3. Что изменится, если параметр типа присутствует среди аргументов функции?

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