In this tutorial we will learn about Functions in GoLang, and look at various ways you can write functions.

This blog is part of the tutorial series – Learn Go, in which we will learn about Go language step by step.

Table of Contents

1.0 Anatomy of functions

Function

  • is a block of executable code
  • can be executed/called from other places in our code
  • can pass arguments to function (if supported)
  • can return values to calling code, after its execution.

A function definition looks like

func [name] ( [parameters] ) [return-types]  {
    [function-body]
}

Note: Function Parameters and Return values are Optional.

 

Top ∆

2.0 Function with no arguments and no return value

Following is an example of a simple function (with no arguments and no return types) – just prints a String message

func helloWord() {
   fmt.Println("Hello World")
}

You can call this function as below

helloWord()

 

 

Top ∆

3.0 Function with only 1 parameter

Now let’s write a function and pass an argument to it.

A function parameter consists of two things

  • name of the parameter
  • data type of the parameter.

Below are some examples of functions with single parameters.

Example 1: Function with single parameter firstName of type string

func test ( firstName string ) {
   ...
}

You can call the above function and pass it argument as below

test ( "Hello World")

Example 2: Function with single parameter number of type int

func test ( number int ) {
   ...
}

You can call the above function and pass it argument as belo

test ( 123 )

 

 

Top ∆

4.0 Function with more than 1 parameters

To pass more than 1 parameter to function, just pass a list of comma separated parameters to the function

Example 1: Function with three integer parameters – x, y , and z

func test ( x int, y int, z int ) {
   ...
}

Note: If the function contains of more than 1 parameter of same data type, they can be clubbed together. So the above function with three integer parameters can be re-written as below

func test ( x, y, z int ) {
   ...
}

To execute the above function test, we can pass arguments to it as below

test (1, 2, 3)

Example 2: Function with three integer parameters – i, j , and k, two string parameters x, y and a boolean parameter b

func test ( i, j, k int, x, y string, b bool ) {
   ...
}

To execute the above function test, we can pass arguments to it as below

test (1, 2, 3, "aaa", "bbb", true)

 

 

Top ∆

5.0 Function with a single return value

Till now we have only seen functions that do not return any values.
Let’s see how to return value from a function.

  • First we need to specify the data type of the returned value.
  • Then we need to return the values using return statement.

Lets write a function that adds two numbers and returns the sum.

func add (x, y int) int {
   return x + y
}

Above functions specifies that it returns an integer.
And return statement is used to return the values to the caller of this function.
We can call this function and use the returned value in this way

sum := add(10, 20)
fmt.Println("Sum = ", sum)

The variable sum is used to hold the return value of the add function.

 

 

Top ∆

6.0 Function with more than 1 return value

For those of you who are coming from Java world, may know that in Java a method can return only 1 value. However in Go Lang you can return more than 1 values.

If there are more than 1 return values of a function, they must be specified within a paranthesis ()

For. e.g. below is a function that takes 3 arguments and returns their sum, average, minimum and maximum.

func calculate(x, y, z int) (int, float64, int, int) {
   sum := x + y + z
   avg := float64(sum) / 3
   min := ... // some code to calculate minimum of x, y, z
   max := ... // some code to calculate maximum of x, y, z

   return sum, avg, min, max
}

We can call this function in following way.

n1, n2, n3, n4 := calculate (5, 7, 2)
fmt.Printf("Sum = %d, Avg = %f, Min = %d, Max = %dn", n1, n2, n3, n4)

 

 

Top ∆

7.0 Function with Named Return types

In Go Lang, we can also provide names to the return types of a function (just as we name the function parameters).

When you provide a name to function return type, Go declares them as variables and initializes them to their respective Zero values.

Let’s rewrite the above example of function return types, but this time using named return types

func calculate(x, y, z int) (sum int, avg float64, min, max int) {
   sum = x + y + z
   avg = float64(sum) / 3
   min = ... // some code to calculate minimum of x, y, z
   max = ... // some code to calculate maximum of x, y, z

   return
}

You can very easily find the differences now from the previous example

  • return types now have names. e.g sum, avg, etc
  • the return statement does not need to specify which variables to return
  • we have used = operator instead of := operator, since the variables were already declared in the function return type definition

 

 

Top ∆

8.0 Variadic function

So far we have seen that we are required to specify the number and type of parameters a function can take. And it is also mandatory to provide all the arguments to a function when it is called.

But what if we want to create an open-ended function that can take any number of parameters?

Such a function that can take any number of arguments is called a variadic function. Have you seen any variadic function yet in your journey of Go Lang so far?

Try recalling the append function on slices.
append function can take any number of arguments and appends them to the given slice. Here is what the append function looks like

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

Another common example is fmt.Println, which can take any number of arguments.

Let’s look at the variadic functions in more details now.

 

8.1 Defining a Variadic function

For a function to be a variadic function, its last parameter must be denoted as ...T, where T is the type of the parameter.

Below are some examples of variadic function

//variadic function that takes any number of integer parameters
func test (args ...int) { ... } 


//variadic function that takes any number of string parameters
func test (args ...string) { ... } 


//variadic function that takes any number of boolean parameters
func test (args ...bool) { ... } 


// function with normal parameters and a variadic parameter
func test (name string, age int, args ...bool) { ... } 

...T parameter should be the last parameter of a function.
If you don’t do so you will get a compile time error – Can only use '...' as final argument in list

8.2 Internals of Variadic function and how to use it

  • Internally Go Lang converts the ...T parameter to a slice []T. So,
    •  ...int is convert to []int,
    • ...string is converted to []string, and so on.
  • Since the parameters are actually converted to a slice, so you can always refer to the values using range keyword on the slice.
  • Simplest way of passing arguments to a variadic function is to pass it as comma separated list of arguments. e.g for ...int, you can pass arguments as 1, 2, 3, 4 , etc.

Let’s see a concrete example now on variadic functions.

func sum(numbers ...int) (sum int) {

   fmt.Printf("Data type of argument is %Tn", numbers)

   for num := range numbers {
      sum += num
   }

   return
}

You can see how we are referring to the arguments using range keyword.

Let’s now call the above function from our main function

func main() {

   sum3Odd := sum(1, 3, 5)

   fmt.Println("Sum of 3 Odd numbers = ", sum3Odd)

   sum10 := sum(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)

   fmt.Println("Sum of 10 numbers = ", sum10)
}

It will print following output

Data type of argument is []int
Sum of 3 Odd numbers = 3
Data type of argument is []int
Sum of 10 numbers = 45

As you can see from the output, the type of argument is printed as []int, which means a slice of integers.

 

 

Before we wrap up variadic functions, we would like to answer one more question that may come to your mind –> If ...T internally behaves like []T, then can we pass a slice as an argument to a variadic function?

Then answer is yes we can pass a slice, but we have to unpack the slice using ... operator after the slice. Please see below example

numbers := []int {1, 2, 3, 4, 5}

sum5Num := sum(numbers...)

fmt.Println("Sum of 5 numbers = ", sum5Num)

 

To summarise
...T packs all the arguments in to a slice []T

[]T... unpacks the arguments, i.e., the exact opposite of above operation.

 

 

Top ∆

9.0 Defer statements in Functions

If you are coming from Java world, you might be aware of finally keyword, that is used with try/catch.

GoLang does not have any such try/catch or finally keywords. But it has a similar keyword – defer, that is commonly used to simplify functions that perform various clean-up actions

Deferred function has three important properties

  • Deferred functions are evaluated, when the surrounding function call returns.
  • Deferred function calls are executed in Last In First Out order after the surrounding function returns.
  • A deferred function’s arguments are evaluated when the defer statement is evaluated.
  • defer can only be used on function calls and not statements.
    e.g if you defer a statement like this – defer i = i +2, you will get compile time error – Expression in defer must be a function call

Lets examine the above properties one by one, by looking at examples.

Here is our first example

func test() {
   fmt.Println(1)
   defer fmt.Println(2)
   defer fmt.Println(3)
   fmt.Println(4)
}

As you can see we deferred the second and third Println() statements.

On execution this will print following output.

1
4
3
2

Now let’s understand why this output is printed as above

  • The deferred Println() statements are not executed until the surrounding test() function finishes. Hence the test function first prints 1 and then 4.
  • Since the deferred Println() function calls are put in stack and executed in LIFO order, hence the order in which they are printed are 3 and then 2.

 

Now let’s look at another example, to see when is the deferred function’s arguments are evaluated.

func deferTest() {
   i, j := 5, 10
   defer fmt.Println("First", i, j)
   i = i * 2
   j = j * 2
   fmt.Println("Last", i, j)
}

The above function when executed will print following output

Last 10 20
First 5 10

Although the deferred function is executed at end of the surrounding function call, their arguments are evaluated when they are actually deferred.
Hence the deferred function prints First 5 10 and not First 10 20

 

Before we leave the topic of deferred function calls, lets look at a real world example where defer function is really helpful.

func readFile() {
   file, _ := os.Open("file.txt")
   defer file.Close()

   ...
   ...
   // Remaining code to
   // read the file and perform other operations
   ...
   ...
}

If you look at above example, we had opened a file and then deferred it close() operation. This helps us to make sure that we do not make a mistake of not closing the file, when the function call finishes.

 

Top ∆

10.0 Function Exports

We had already looked at Packages, in earlier blog.

Now let’s look at how we can export functions from one package to other.

If a function name starts with Capital Letter, it is automatically exported. Else it is internal to the package.

Lets see an example.

Create following directory structure and Go files, within your project

.
├── p1
│   └── Common.go
└── p2
    └── MainTest.go

 

In Common.go, file of package p1, we have written two functions.

package p1

//Name begins with lowercase. Cannot access outside package
func add(x, y int) int {
   return x + y
}

//Name begins with uppercase. Can access outside package
func Sum(x, y int) int {
   return x + y
}

 

In MainTest.go file of package p2, create a main function and try to access functions from package p1

package main

import (
   "LearningGo/p1"  //LearningGo is my project name
)

func main() {

   p1.Sum(5, 10)

   // Will throw compile time error - Unresolve reference.
   p1.add(5, 10)  
   
}

As you can see from above example, only those functions whose name begin with Upper case letters are exported automatically and available for use from other functions.

Top ∆


In the next blog of the Learn Go tutorial series, we will look at Structs in Go