Variadic-функции в Go позволяют принимать переменное количество аргументов одного типа благодаря синтаксису с троеточием .... Например:
func sum(nums ...int) int { total := 0 for _, num := range nums { total += num } return total }
Вызывать такую функцию можно с любым количеством аргументов:
result := sum(1, 2, 3, 4) // 10
Или передать срез с использованием синтаксиса ...:
numbers := []int{1,2,3} result := sum(numbers...) // 6
Тонкости:
[]T). Это значит, можно передать внутрь уже подготовленный срез...., функция ожидает обычный параметр типа []T, а не variadic.Какой результат даст следующий код и почему?
func printInts(nums ...int) { fmt.Printf("%#v ", nums) } ints := []int{1, 2, 3} printInts(ints)
Многие ошибочно считают, что этот код скомпилируется, но на самом деле он вызовет ошибку компиляции:
Ошибка: нельзя передать срез без ... в variadic-параметр.
Правильно:
printInts(ints...) // OK
История
На крупном проекте функцию логирования описали как func Log(msgs ...string). При передаче заранее подготовленного среза строк вызывали просто Log(strings), получая некорректный вывод лога или panic во время работы. Причина — отсутствие ..., так что функция воспринимала один аргумент типа []string, а не отдельные строки.
История
В одной из utility функций требовалось обработать параметры, среди которых был срез, и добавить к нему больше элементов внутри. Ожидали, что ... создаёт копию среза, а не передаёт ссылку — но внутри функции изменяли срез, что неожиданно влияло на вызывающую сторону, особенно если исходный срез был переиспользуемым буфером.
История
Разработчик реализовал функции агрегирования, используя variadic-параметр, но один из коллег неверно использовал синтаксис вызова через кастинг: sum(interface{}([]int)), что приводило к неявным ошибкам времени исполнения, тогда как правильно было бы использовать явно sum(slice...). Незнание нюансов вызывало неочевидные баги при массовых рефакторингах.