Variadic Functions

Functions that accept any number of values

3-minute read
Made by ChickenFryBytes Studios
Table of Contents

These are functions that can accept any number of arguments. We have already seen Println from the fmt package. It can accept many values and string them together to be outputted into the console. Here is a function that accepts any number of integers and returns their product:

package main

import "fmt"

func Product(numbers ...int) int {
	product := 1

	for _, number := range numbers {
		product *= number
	}
	return product
}

func main() {
	a := Product(4, 5, 2)
	b := Product(-1, 2, 5)
	fmt.Println("a:", a)
	fmt.Println("b:", b)
}
variadic-product.go
Copy

Defining the function this way allows us to access the values in the numbers variable as if they were in a slice. In fact, we can pass a slice of integers into the function in the following manner and it will still work:

package main

import "fmt"

func Product(numbers ...int) int {
	product := 1

	for _, number := range numbers {
		product *= number
	}
	return product
}

func main() {
	set := []int{4, 5, 2}
	a := Product(set...)
	fmt.Println("a:", a)
}
variadic-product-slice.go
Copy

We can have a function that accepts a bunch of strings and returns the concatenation of these strings:

package main

import "fmt"

func Concat(strings ...string) string {
	cat := ""
	for _, str := range strings {
		cat += str
	}
	return cat
}

func main() {
	fmt.Println("result:", Concat())
	fmt.Println("result:", Concat("hello", "there"))
	fmt.Println("result:", Concat("how", "are", "you"))
}
variadic-concat.go
Copy

The last parameter

It is important to note that the parameter that represents the slice containing the numerous values passed into the function must be last in the function definition. The following is not valid:


func myFunction(messages ...string, recipient string){
    // do something
}

func main(){
    myFunction("Hello", "How are you?", "John")
}

In such a case, there is no way for the program to know that John is the recipient. The messages variable must be last to let the compiler know that any number of messages can follow the recipient value:


func myFunction(recipient string, messages ...string){
    // do something
}

func main(){
    myFunction("John", "Hello", "How are you?")
}

Using variadic functions with other variadic functions

Consider if we had to make a function that accepts some expected number of values and passes the other arguments into another variadic function.

For example, you may not want to use fmt.Sprintf in the arguments of another function because it makes the line too wide (never nesting):

package main

import "fmt"

func TellFriends(friends []string, message string) {
	for _, friend := range friends {
		fmt.Printf("[Me To %s]: %s\n", friend, message)
	}
}

func TellFriendsAgain(friends []string, message string, args ...any) {
	if len(args) > 0 {
		message = fmt.Sprintf(message, args...)
	}
	for _, friend := range friends {
		fmt.Printf("(Again) [Me To %s]: %s\n", friend, message)
	}
}

func main() {
	favouriteFood := "pizza"
	friendsList := []string{"Jamie", "Sue", "Mary"}

	TellFriends(friendsList, "Hello there")

	// is wider and harder to read
	TellFriends(friendsList, fmt.Sprintf("Hello my favourite food is %s", favouriteFood))

	// uses variable argument count to encapsulate Sprintf
	TellFriendsAgain(friendsList, "Hello my favourite food is %s", favouriteFood)
}
variadic-fmt.go
Copy

By changing the parameters from:


TellFriends(friends []string, message string)

to:


TellFriendsAgain(friends []string, message string, args ...any)

We are now able to pass these values internally into fmt.Sprintf and avoid having to call it within the function call for TellFriends.

We could have also used the Sprintf function in a previously line but the code would be more verbose. With the new version of the function we simply use it in a similar manner to Sprintf - for every placeholder in the message passed, we use an extra argument.

Like our content? Support us via Donations