Параметр ...T
Поставьте ... перед типом последнего параметра. Тогда вызывающий код может передать любое число аргументов этого типа, в том числе ни одного. Внутри функции параметр является слайсом.
Программа печатает 0, 5 и 10. Без аргументов nums это nil-слайс длины 0, и цикл просто не выполняется.
Обычные параметры могут идти первыми. Вариативным может быть только последний:
func logf(level string, format string, args ...any)
Передача слайса через s...
Если слайс уже есть, передать его напрямую нельзя: sum(nums) падает с cannot use nums (variable of type []int) as int value in argument to sum. Распакуйте его, поставив ... после аргумента:
append тоже вариативная, поэтому склеивание двух слайсов выглядит как append(a, b...).
Распаковка должна быть единственным, что стоит на вариативной позиции. sum(1, nums...) падает с too many arguments in call to sum. Сначала соберите общий слайс через append.
Слайс общий, а не копия
Когда вы вызываете функцию с отдельными аргументами, Go создаёт для них новый слайс. Когда вы распаковываете существующий слайс через s..., функция получает тот же самый слайс. Запись в его элементы меняет данные вызывающего кода.
Вывод:
[7 8 9]
[0 8 9]
Вариативная функция, которая меняет свой параметр, должна это документировать или сначала копировать его через slices.Clone. Большинство вариативных функций только читают аргументы, так что это редко кусается, но это реальное отличие от языков, где rest-параметры всегда новый массив.
...any и как работает fmt.Println
fmt.Println объявлена как func Println(a ...any) (n int, err error). any (псевдоним interface{} с Go 1.18) принимает любой тип, поэтому в одном вызове можно передать строки, числа и структуры.
Важная деталь это пробрасывание: logf передаёт args... в Printf. Без точек Printf получит один аргумент, []any, и строка предупреждения напечатается как [WARN] [3 8] of %!d(MISSING) workers idle. go vet сообщает missing ... in args forwarded to printf-like function, только когда параметр формата передаётся без изменений, как в fmt.Printf(format, args). Здесь формат собирается через +, поэтому vet молчит, и за точками следить вам.
[]string нельзя распаковать в ...any. Типы элементов должны совпадать точно, поэтому сначала преобразуйте:
names := []string{"a", "b"}
args := make([]any, len(names))
for i, n := range names {
args[i] = n
}
fmt.Println(args...)
Хотя бы один аргумент
Вариативный параметр допускает ноль аргументов. Если функции нужен хотя бы один, сделайте первый обычным параметром:
Теперь maxOf() это ошибка компиляции, а не проверка во время выполнения. Встроенная max устроена так же: ей нужен хотя бы один аргумент.
Вариативные параметры как необязательные аргументы
Значений параметров по умолчанию в Go нет. Иногда вариативным параметром имитируют один необязательный аргумент:
func connect(addr string, timeout ...time.Duration)
Это работает, но читается плохо: вызывающий код может передать три таймаута, и сигнатура не говорит, какой из них победит. Если опций больше одной, лучше структура с конфигурацией или шаблон функциональных опций (...Option, где каждая Option это функция, которая задаёт поле). Этот шаблон тоже вариативный, но у каждого аргумента понятный смысл.
Справка по правилам
| Правило | Пример |
|---|---|
| Вариативным может быть только последний параметр | func f(a string, b ...int) |
| Внутри параметр является слайсом | b имеет тип []int |
| Без аргументов получается nil-слайс | f("x") даёт b == nil |
Слайс распаковывается через ... | f("x", nums...) |
| Распаковка разделяет базовый массив | запись в b[i] меняет nums |
| Нельзя смешивать распаковку с дополнительными значениями | f("x", 1, nums...) не компилируется |
Часто задаваемые вопросы
Что такое вариативная функция в Go?
Функция, последний параметр которой имеет вид ...T, например func sum(nums ...int) int. Её можно вызвать с нулём или более аргументов типа T, а внутри функции параметр является слайсом []T.
Как передать слайс в вариативную функцию в Go?
Добавьте ... после слайса: sum(nums...). Слайс передаётся как есть, без копирования, поэтому функция видит тот же базовый массив. Смешивать распаковку с отдельными аргументами нельзя: sum(1, nums...) не компилируется.
Есть ли в Go spread-оператор?
Только для последнего аргумента вариативного вызова: f(s...). Общей распаковки для сборки массивов, структур или вызова невариативных функций со слайсом нет. append(a, b...) это то же правило, применённое к встроенной append.
Может ли у функции в Go быть два вариативных параметра?
Нет. Вариативным может быть только последний параметр, и он один на функцию. Дополнительные списки передавайте обычными параметрами-слайсами.