Variant functions in Go

fade by clockbirds



The Mail.ru Cloud Solutions team hastranslated an article about variadic functions in Go. Its author explains how variadic functions differ from ordinary functions and how to create them.



What is a variadic function



A function is a piece of code designed to perform a specific task. The function is passed one or more arguments, and it returns one or more results.



Variant functions are the same as regular functions , but they can take an infinite or variable number of arguments.



func f(elem ...Type)


This is what the typical variadic function syntax looks like. The operator ..., also known as the boxing operator, tells Go to store all type arguments Typein a parameter elem. In other words, Go creates a variable elemwith a type []Type, that is, a slice. And all the arguments passed to the function are stored in the slice elem.



Let's see an example of a function append().



append([]Type, args, arg2, argsN)


The function appendexpects the first argument to be a slice of type Typefollowed by an arbitrary number of arguments. But how do you add a slice s2to a slice s1?



From the definition of the function, append()it follows that we cannot pass two slices to it - there is Typeonly one type argument . Therefore, we use the unboxing operator ...to unpack the slice into a series of arguments. They can then be passed to the function append.



append(s1, s2...)


...denote both the packing operator and the unpacking operator. The unboxing operator always appears after the variable name.



In our example, the slices s1and s2the same type. We usually know the parameters of a function and the number of arguments it takes. How does a function acceptknow how many parameters have been passed to it?



If you look at the function declaration append, you can see the expression elems ...Type. It packs all arguments, starting with the second, into a slice elems.



func append(slice []Type, elems ...Type) []Type


Only the last argument in a function declaration can be variadic.



Hence, the first argument to the function appendis only a slice, since it is defined as a slice. All subsequent arguments are packed into one argument named elems.



Now let's see how to create your own variadic function.



How to create a variadic function



As mentioned above, a variadic function is a regular function that takes a variable number of arguments. To do this, you need to use the boxing operator ...Type.



Let's write a function getMultipleswith a first factortype argument int(multiplication factor) and a variable number of additional type arguments int. They will be packed into a slice args.



Use to makecreate an empty slice, it is the same size as the slice args. Using a loop for range, multiply the slice elements argsby factor. We put the result into a new empty slice multiples, which we will return as a result of the function.



func getMultiples(factor int, args ...int) []int {
	multiples := make([]int, len(args))
	for index, val := range args {
		multiples[index] = val * factor
	}
	return multiples
}


This is a very simple function, apply it inside a block 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



What happens if, in the example above, you pass the slice s to the functiongetMultiplesas the second argument? The compiler will report an error:getMultiplesit cannot be useds (type [] int)as a typein an argumentint. This is because the slice has a type that[]int, getMultiplesexpects parameters fromint.



How a slice is passed to a variadic function



Slice is a reference to an array. So what happens when you pass a slice to a variadic function using the unboxing operator? Does Go create a new slice argsor does it use an old slice s?



Since we have no comparison tools args == s, we need to change the slice args. Then we find out if the original slice has changed s.





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



In the example above, we slightly changed the function getMultiples. And instead of creating a new slice, we assign the calculation results to the elements of the slice args, replacing the incoming elements with the multiplied elements.



As a result, we see that the values โ€‹โ€‹of the original sliceshave changed. If you pass arguments to the variadic function via the unboxing operator, then Go uses references to the data array underlying the original to create a new slice. Be careful not to make a mistake, otherwise the original data will change as a result of calculations.



Good luck!



What else to read:



  1. Go and CPU caches
  2. Why a backend developer should learn the Go programming language
  3. Our telegram channel about digital transformation



All Articles