[Перевод] Вариативные функции в Go

esklgbunpmjr2ofpzhmi2msq9ww.jpeg

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)
}


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.

fqn5ylpif_w11lhwyjqxi72f29i.png
https://play.go.org/p/fbNgVGu6JZO

В примере выше мы немного изменили функцию getMultiples. И вместо создания нового слайса присваиваем результаты вычислений элементам слайса args, заменяя входящие элементы умноженными элементами.

В итоге видим, что значения исходного слайса s изменились. Если передавать аргументы в вариативную функцию через оператор распаковки, то Go использует для создания нового слайса ссылки на массив данных, лежащий в основе оригинального. Будьте внимательны, чтобы не допустить ошибку, иначе исходные данные изменятся в результате вычислений.

Удачи!

Что еще почитать:

  1. Go и кэши CPU
  2. Почему бэкенд-разработчику стоит выучить язык программирования Go
  3. Наш телеграм-канал о цифровой трансформации

© Habrahabr.ru