Learning about functions
Functions in Go are what you'd expect from a modern programming language. There are only a few things that make Go functions different:
- Multiple return values are supported
- Variadic arguments
- Named return values
The basic function signature is as follows:
func functionName([varName] [varType], ...) ([return value], [return value], ...){ }
Let's make a basic function that adds two numbers together and returns the result:
func add(x int, y int) int { return x + y }
As you can see, this takes in two integers, x
and y
, adds them together, and returns the result (which is an integer). Let's show how we can call this function and print its output:
result := add(2, 2) fmt.Println(result)
We can simplify this function signature by declaring both x
and y
types with a single int
keyword:
func add(x, y int) int { return x + y }
This is equivalent to the previous one.
Returning multiple values and named results
In Go, we can return multiple values. For example, consider a function that divides two integers and returns two variables, the result and the remainder, as follows:
func divide(num, div int) (res, rem int) { result = num / div remainder = num % div return res, rem }
This code demonstrates a few new features in our function:
- Argument
num
is the number to be divided - Argument
div
is the number to divide by - Return value
res
is the result of the division - Return value
rem
is the remainder of the division
First is named returns (res
and rem
). These variables are automatically created and ready for use inside the function.
Notice I use =
and not :=
when doing assignments to those variables. This is because the variable already exists, and we want to assign a value (=
). :=
means create and assign. You can only create a new variable that doesn't exist. You will also notice that now the return type is in parenthesis. You will need to use parenthesis if you use more than one return value or named returns (or in this case, both).
Calling this function is just as simple as calling add()
before, as shown here:
result, remainder := divide(3, 2) fmt.Printf("Result: %d, Remainder %d", result, remainder)
Strickly speaking, you don't have to use return
to return the values. However, doing so will prevent some ugly bugs that you will eventually encounter.
Next, we will look at how we can have a variable number of arguments as function input that allows us to create functions such as fmt.Println()
, which you have been using in this chapter.
Variadic arguments
A variadic argument is when you want to provide 0
to infinite arguments. A good example would be calculating a sum of integers. Without variadic arguments, you might use a slice (a growable array type, which we will talk about later), as follows:
func sum(numbers []int) int { sum := 0 for _, n := range numbers { sum += n } return sum }
While this is fine, using it is cumbersome:
args := []int{1,2,3,4,5} fmt.Println(sum(args))
We can accomplish this same thing by using the variadic (...
) notation:
func sum(numbers ...int) int { // Same code }
numbers
is still []int
, but has a different calling convention that is more elegant:
fmt.Println(sum(1,2,3,4,5))
Note
You can use variadic arguments with other arguments, but it must be the last argument in the function.
Anonymous functions
Go has a concept of anonymous functions, which means a function without a name (also called a function closure).
This can be useful to take advantage of special statements that honor function boundaries, such as defer
, or in goroutines
. We will show how to take advantage of these for goroutines
later, but for now let's show how to execute an anonymous function. This is a contrived example that is only useful in teaching the concept:
func main() { result := func(word1, word2 string) string { return word1 + " " + word2 }("hello", "world") fmt.Println(result) }
This code does the following:
- Defines a single-use function (
func(word1, word2 string) string
) - Executes the function with the
hello
andworld
arguments - Assigns the
string
return value to theresult
variable - Prints
result
Now that we have arrived at the end of this section, we have learned about how Go functions are declared, the use of multiple return values, variadic arguments for simplified function calling, and anonymous functions. Multiple return values will be important in future chapters where we deal with errors, and anonymous functions are key components of our future defer
statements and for use with concurrency.
In the next section, we will explore public and private types.