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
- 2.0 Function with no arguments and no return value
- 3.0 Function with only 1 argument
- 4.0 Function with more than 1 arguments
- 5.0 Function with a single return value
- 6.0 Function with more than 1 return value
- 7.0 Function with Named Return values
- 8.0 Variadic function
- 9.0 Defer statements in Functions
- 10.0 Function Exports
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.
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()
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 )
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)
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.
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)
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
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 usingrange
keyword on theslice
. - 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 as1, 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.
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 surroundingtest()
function finishes. Hence thetest
function first prints1
and then4
. - Since the deferred
Println()
function calls are put in stack and executed inLIFO
order, hence the order in which they are printed are3
and then2
.
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.
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.
In the next blog of the Learn Go tutorial series, we will look at Structs in Go