[Перевод] Вариативные функции в Go
fade by clockbirds
Команда 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://play.go.org/p/BgU6H9orhrn
Что произойдет, если в примере выше передать слайс s функции getMultiples
в качестве второго аргумента? Компилятор будет сообщать об ошибке: в аргументе getMultiples
он не может использовать s (type [] int)
в качестве типа int
. Так происходит, поскольку слайс имеет тип []int, а getMultiples
ожидает параметры из int
.
Как слайс передается в вариативную функцию
Слайс — ссылка на массив. Так что же происходит, когда вы передаете слайс в вариативную функцию, используя оператор распаковки? Создает ли Go новый слайс args
или использует старый слайс s
?
Поскольку у нас нет инструментов для сравнения, args == s
, нужно изменить слайс args
. Тогда узнаем, изменился ли оригинальный слайс s
.
https://play.go.org/p/fbNgVGu6JZO
В примере выше мы немного изменили функцию getMultiples
. И вместо создания нового слайса присваиваем результаты вычислений элементам слайса args
, заменяя входящие элементы умноженными элементами.
В итоге видим, что значения исходного слайса s
изменились. Если передавать аргументы в вариативную функцию через оператор распаковки, то Go использует для создания нового слайса ссылки на массив данных, лежащий в основе оригинального. Будьте внимательны, чтобы не допустить ошибку, иначе исходные данные изменятся в результате вычислений.
Удачи!
Что еще почитать:
- Go и кэши CPU
- Почему бэкенд-разработчику стоит выучить язык программирования Go
- Наш телеграм-канал о цифровой трансформации