Go: Функции
Теория: Variadic-функции
В реальных программах редко бывает так, что мы всегда знаем точное количество входных данных. Иногда это одно значение, иногда пять, иногда ни одного. Если делать отдельные функции под каждый случай — код быстро превратится в кашу.
Чтобы этого избежать, в Go есть variadic-функции — функции с переменным числом аргументов. Они позволяют передавать сколько угодно значений одного типа. Под капотом это всё тот же срез ([]T), просто собранный компилятором.
Базовый пример: сумма чисел
Если не передать ни одного аргумента, nums станет пустым срезом ([]int{}), и цикл просто не выполнится.
Передача готового среза
Часто у нас уже есть []int, и мы хотим передать его в variadic-функцию. В этом случае нужен ... - оператор распаковки среза.
Если забыть ..., компилятор сообщит об ошибке: cannot use numbers (type []int) as type int in argument to Sum.
Логирование
В логах мы часто пишем сообщение и дополнительные детали, которых может быть 0, 1 или больше. Variadic-функции подходят идеально.
SQL-запрос с фильтрами
Допустим, у нас есть функция, которая собирает условие WHERE из произвольного количества фильтров.
Здесь мы не ограничиваем разработчика: он может передать хоть один фильтр, хоть двадцать.
Несколько аргументов и variadic
Variadic-параметр всегда стоит последним в списке аргументов. До него можно указывать обычные параметры, а уже затем вариативные.
Variadic в стандартной библиотеке
Многие знакомые функции устроены именно так. fmt.Println(a ...any) печатает любое количество значений разных типов. А append(slice, elems ...T) добавляет к срезу сразу несколько элементов.
Под капотом: что делает компилятор
Когда мы вызываем:
Go превращает это в:
То есть variadic-параметр — это всегда срез. В этот момент создаётся новый срез длиной три, в него копируются все значения, а в функцию передаётся структура среза (pointer, length, capacity). Если у нас уже есть срез и мы пишем f(slice...), то ничего не копируется — передаётся тот же самый срез.
Пустые вызовы
Компилятор подставит:
Это значит, что nums будет пустым срезом ([]int{}), а не nil.
Подводные камни
Внутри функции variadic-параметр всегда превращается в срез, поэтому работать с ним нужно как с []T. При передаче готового среза обязательно использовать оператор ..., иначе компилятор выдаст ошибку. И помнить, что в списке аргументов может быть только один variadic-параметр, и он всегда должен быть последним.
Итоги
Variadic-функции в Go — это способ передавать переменное количество аргументов. Они упрощают API функций, когда заранее неизвестно количество данных, и позволяют не писать десятки перегрузок. Такой подход активно используется в стандартной библиотеке — например, в функциях fmt и в append. По сути это обычный срез, собранный автоматически компилятором. Главное — помнить, что variadic-параметр всегда идёт последним и обрабатывается как []T.


