![](https://habrastorage.org/webt/es/kl/gb/esklgbunpmjr2ofpzhmi2msq9ww.jpeg)
Команда Mail.ru Cloud Solutions перевела статью о вариативных функциях в Go. Ее автор рассказывает, чем вариативные функции отличаются от обычных и как их создавать.
Что такое вариативная функция
Функция — фрагмент кода, предназначенный для выполнения конкретной задачи. Функции передают один или несколько аргументов, а она возвращает один или несколько результатов.
Вариативные функции — такие же, что и обычные, но они могут принимать бесчисленное или переменное число аргументов.
func f(elem ...Type)
Так выглядит типичный синтаксис вариативной функции. Оператор
...
, который еще называют оператором упаковки, указывает Go сохранить все аргументы типа Type
в параметре elem
. Другими словами, Go создает переменную elem
c типом []Type
, то есть слайс. И все аргументы, передаваемые функции, сохраняются в слайсе elem
.Давайте посмотрим на примере функции
append()
.append([]Type, args, arg2, argsN)
Функция
append
ожидает, что первым аргументом будет слайс типа Type
, за которым следует произвольное число аргументов. Но как добавить слайс s2
к слайсу s1
?Из определения функции
append()
следует, что мы не можем передать ей два слайса — аргумент типа Type
только один. Поэтому мы используем оператор распаковки ...
, чтобы распаковать слайс в серию аргументов. Их затем можно передать в функцию append
.append(s1, s2...)
...
обозначают и оператора упаковки, и оператора распаковки. Оператор распаковки всегда стоит после имени переменной.В нашем примере слайсы
s1
и s2
одного типа. Обычно мы знаем параметры функции и количество аргументов, которые она принимает. Как же функция accept
понимает, сколько параметров ей передали?Если посмотреть на объявление функции
append
, то можно увидеть выражение elems ...Type
. Оно упаковывает все аргументы, начиная со второго, в слайс elems
.func append(slice []Type, elems ...Type) []Type
Только последний аргумент в объявлении функции может быть вариативным.
Значит, первый аргумент функции
append
— только слайс, поскольку он определен как слайс. Все последующие аргументы упакованы в один аргумент под названием elems
.Теперь посмотрим, как создать собственную вариативную функцию.
Как создать вариативную функцию
Как упоминалось выше, вариативная функция — обычная функция, которая принимает переменное число аргументов. Чтобы это сделать, нужно использовать оператор упаковки
...Type
.Давайте напишем функцию
getMultiples
с первым аргументом factor
типа int
(фактор мультипликации) и переменным числом дополнительных аргументов типа int
. Они будут упакованы в слайс args
.При помощи
make
создаем пустой слайс, он такого же размера, как и слайс args
. Используя цикл for range
, умножаем элементы слайса args
на factor
. Результат помещаем в новый пустой слайс multiples
, который и вернем как результат работы функции.func getMultiples(factor int, args ...int) []int {
multiples := make([]int, len(args))
for index, val := range args {
multiples[index] = val * factor
}
return multiples
}
Это очень простая функция, применим ее внутри блока
main()
.func main() {
s := []int{10, 20, 30}
mult1 := getMultiples(2, s...)
mult2 := getMultiples(3, 1, 2, 3, 4)
fmt.Println(mult1)
fmt.Println(mult2)
}
![](https://habrastorage.org/webt/wj/ji/l3/wjjil3pg01j0c6gbt7cufknp9_o.png)
https://play.go.org/p/BgU6H9orhrn
Что произойдет, если в примере выше передать слайс s функции
getMultiples
в качестве второго аргумента? Компилятор будет сообщать об ошибке: в аргументе getMultiples
он не может использовать s (type [] int)
в качестве типа int
. Так происходит, поскольку слайс имеет тип []int, а getMultiples
ожидает параметры из int
.Как слайс передается в вариативную функцию
Слайс — ссылка на массив. Так что же происходит, когда вы передаете слайс в вариативную функцию, используя оператор распаковки? Создает ли Go новый слайс
args
или использует старый слайс s
?Поскольку у нас нет инструментов для сравнения,
args == s
, нужно изменить слайс args
. Тогда узнаем, изменился ли оригинальный слайс s
.![](https://habrastorage.org/webt/fq/n5/yl/fqn5ylpif_w11lhwyjqxi72f29i.png)
https://play.go.org/p/fbNgVGu6JZO
В примере выше мы немного изменили функцию
getMultiples
. И вместо создания нового слайса присваиваем результаты вычислений элементам слайса args
, заменяя входящие элементы умноженными элементами.В итоге видим, что значения исходного слайса
s
изменились. Если передавать аргументы в вариативную функцию через оператор распаковки, то Go использует для создания нового слайса ссылки на массив данных, лежащий в основе оригинального. Будьте внимательны, чтобы не допустить ошибку, иначе исходные данные изменятся в результате вычислений.Удачи!
Что еще почитать: