Functions and Pointers

Functions

Go, does not have keywords like private, public, protected and final etc and Go does not have concept of inheritance so it is very important to understand how functions can be used to do things that we ideally do when we use any other language.

Keyword func, is used for defining function. We have already seen the function main which is starting point of every application.

func main() {
	fmt.Println("Hello, playground")
}

Function main is a special function, and when defined in package main, it is used as a starting point of your application. Function main neither accepts any input nor does it return any value. As soon as function main completes execution, the program exits.

Similar to function main, we can declare a custom function, which can accept an input and returns a value as follows:

func funcName(i int) int {
	return i
}

Here we have declared function funcName, which accepts i of type int as input and returns the same value back.

Return multiple values

Now let us modify function funcName to accept 2 input parameters and return them.

func funcName(i int, s string) (int, string) {
	return i, s
}

Function funcName, returns back 2 values of type int and string. It is possible to return multiple values from a function. Caller of this function can chose whether to use all the returned value. In case if you do not want to use all the returned values use Blank Identifier (”_” Underscore) to skip. As shown below:

i, _ = funcName(10, "ABC")

This will skip 2nd value returned from function.

Let us add one more parameter of type int to funcName:

func funcName(i, j int, s string) (int, int, string) {
	return i, j, s
}

Now function funcName is accepting 3 parameters, 2 of type int (i and j) and one of type string. Note the way in which i and j are declared.

Anonymous (inline)

func main() {
	i, j := 10, 20
	c := func(a, b int) int {
		return a + b
	}(i, j)

	fmt.Println("Total :", c)
}

http://play.golang.org/p/POfVLVe9je

Output:

Total : 30

Here we have created an inline function for adding two numbers. Function takes 2 values as input and returns result of addition, which we are assigning to variable c. Check (I, j) at the end of the inline function after “}”, this will invoke the function immediately after declaration.

But in case if that’s not the requirement, we can do something as follows:

func main() {

	i, j := 10, 20

	c := func(a, b int) int {
		return a + b
	}

	fmt.Println("Total :", c(i, j))
}

http://play.golang.org/p/0Ya9JnvI0_

Output:

Total : 30

Here we have declared inline function, and assigned it to c, now c is pointing to inline function which we are invoking in Println statement via c(i, j).

Function with Named Return

func main() {
	i, j := 9, 2
	result, remainder := divide(i, j)
	fmt.Printf("Divide %d by %d. Result %d, remainder %d", i, j, result, remainder)
}

func divide(i, j int) (result int, mod int) {
	if j != 0 {
		result = i / j
		mod = i % j
	}
	return
}

http://play.golang.org/p/_CyIxAO7R5

Output:

Divide 9 by 2. Result 4, remainder 1

We have declared function divide, for dividing 2 integers and return result and remainder. Check the way we have declared values to be returned for function divide. We have given them name result and remainder and used them in function for holding result and remainder respectively. Note that in this case we are not passing values to be returned in return statement at the end of the function. Since we have named return values in function declaration, Go will internally return them.

Note: Named parameters will be initialized to their Zero values by Go. In this case since result and mod are declared as int they will be initialized to 0. Now if we do not use them in function, 0 will be returned.

Variadic Function (Arbitrary Number of Parameters)

func main() {
	fmt.Printf("Total = %d", computeTotal(1, 2, 3, 4, 5, 6))
}

func computeTotal(nums ...int) (total int) {
	for _, i := range nums {
		total += i
	}
	return
}

http://play.golang.org/p/SIXdL9-3AJ

Output:

Total = 21

Function computeTotal, accepts arbitrary number of integers, add them and returns total. Key things to note here is the way to pass arbitrary number of parameters (we used the …int symbol to notify that it is a series of integers), named return value and use of Blank Identifier. Also note, that arbitrary parameter should be last parameter in the list of parameters passed (right most parameter).

Pass By Value

When values are passed to a function they are passed by value i.e. new copy of the type is created and passed as parameter to the function.

Let us look at the example:

func main() {

	i := 10
	fmt.Println("Value in main before calling function :", i)
	computeAndPrint(i)
	fmt.Println("Value in main after calling function :", i)
}

func computeAndPrint(i int) {
	i = 100
	fmt.Println("Value in function :", i)
}

http://play.golang.org/p/zmxQBKgd44

Output:

Value in main before calling function : 10
Value in function : 100
Value in main after calling function : 10

We created variable i of type int in function main and initialized it to 10. Then we called the function computeAndPrint, in which we changed the value of i to 100 and printed it.

Now if you check the output, you can see value of i as 10 when printed in function main before and after calling computeAndPrint function. And when printed within the function computeAndPrint it is printed as 100 since on 1st line in the function computeAndPrint, we are updating the value of i to 100.

This shows that values passed as parameter to function are passed as value.

Let us look at another example:

type Person struct {
	FirstName string
	LastName  string
}

func main() {
	p := Person{"ABC", "XYZ"}
	fmt.Println("Value of Person in main (before swap) :", p)
	swapAndPrint(p)
	fmt.Println("Value of Person in main (after swap) :", p)
}

func swapAndPrint(p Person) {
	p.FirstName, p.LastName = p.LastName, p.FirstName
	fmt.Println("Value of Person in function :", p)
}

http://play.golang.org/p/GSdAd2HHwm

Output:

Value of Person in main (before swap) : {ABC XYZ}
Value of Person in function : {XYZ ABC}
Value of Person in main (after swap) : {ABC XYZ}

We have struct Person, with 2 fields FirstName and LastName. In main function we create instance for Person and print it, then we call swapAndPrint function, which will swap FirstName and LastName and print it.

After calling swapAndPrint, we again print Person in function main.

From output you can see, that before and after call to swapAndPrint function, FirstName and **LastName **are printed as same as what they were set while creating instance of Person. While in swapAndPrint function, we have swapped FirstName and LastName.

This is because; when Person is passed to function a new copy of Person is created and passed. Hence any change in within the function does not get reflected outside the function i.e. in main function.

Function Vs Method

Ideally the terms function and method are used interchangeably, but that’s not correct. If functions are defined within class and called on object of that class they are called as methods.  Go is not OO language and does not have concept of class. In Go, if function is called upon struct it is called as method. Let us modify the function swapAndPrint in above example to method.

type Person struct {
	FirstName string
	LastName  string
}

func main() {

	p := Person{"ABC", "XYZ"}
	fmt.Println("Value of Person in main (before swap) :", p)
	p.swapAndPrint()
	fmt.Println("Value of Person in main (after swap) :", p)

}

func (p Person) swapAndPrint() {
	p.FirstName, p.LastName = p.LastName, p.FirstName
	fmt.Println("Value of Person in function :", p)

}

http://play.golang.org/p/wtl5Ziwr0B

Output:

Value of Person in main (before swap) : {ABC XYZ}
Value of Person in function : {XYZ ABC}
Value of Person in main (after swap) : {ABC XYZ}

Note the signature of function swapAndPrint,

func (p Person)swapAndPrint()

Here the preceding (p Person), is called as receiver for function swapAndPrint i.e.  swapAndPrint is a method and can only be called on Person.

Check the way method swapAndPrint is called in comparison to the previous example.

Pointers

Pointers are nothing but a variable holding memory address. Using pointers one can directly access memory location. Those who have used or know C/C++ should be familiar with the concept of pointers.

As we have seen above, in Go when we pass parameters to a function, a copy of the parameter is passed and hence it is immutable i.e. any changes made within the function are not available to the caller. Since Pointers allow direct access to memory, using Pointers can save creating of copy, which can directly have a performance impact. But using Pointers will allow function to change the value, so depending upon the need, we need to wisely pick whether to use Pointers or not.

Let us look at example:

func main() {

	i := 10
	p2i := &i

	fmt.Println("Value of i :", i)
	fmt.Println("Value of Pointer to i :", p2i)
	fmt.Println("Value of i via Pointer :", *p2i)

	*p2i = 100
	
	fmt.Println("----------After Updating----------")
	fmt.Println("Value of i :", i)
	fmt.Println("Value of Pointer to i :", p2i)
	fmt.Println("Value of i via Pointer :", *p2i)
}

http://play.golang.org/p/DJlKQhuSK1

Output:

Value of i : 10
Value of Pointer to i : 0x104382e0
Value of i via Pointer : 10
———-After Updating———-
Value of i : 100
Value of Pointer to i : 0x104382e0
Value of i via Pointer : 100

Pointer as Receiver

We have learnt so far that when passing parameters to function, Go passes by value. But if we have a need to modify value of the original element or if element is a complex and deep struct, copying it may have performance impact. In this case it will be ideal to use Pointers. Pointer can be passed as parameter to the function or can act as receiver to function.

Let see an example for pointer as receiver to function:

type Person struct {
	Name  string
	DOB   string
	adult bool
}

func main() {

	p := Person{Name: "ABC", DOB: "1983-11-01"}
	ptr := &p
	ptr.checkIfAdult()
	fmt.Println(*ptr)

}

func (p *Person) checkIfAdult() {
	t, err := time.Parse("2006-01-02", p.DOB)
	if err == nil {
		if t.Year() < 1995 {
			p.adult = true
		}
	}
}

http://play.golang.org/p/uMCJYrGprY

Output:

{ABC 1983-11-01 true}

Method checkIfAdult, checks the year in DOB.If before 1995, it will treat that person as adult and will set adult flag to true. Here we have used “time”, for parsing DOB. First parameter to Parse method is layout, used to define the format. Second parameter is the actual string to be parsed. Method Parse return 2 values, time and error. We check if error is nil, means parsing was successful then check year in DOB to see if it is before 1995.

Method checkIfAdult, is defined on receiver Pointer of type Person. Hence see the way we are calling this method from function main.

In this example in addition to package “fmt” we are importing package “time” for parsing DOB. You can import package as follow:

import "fmt"
import "time"

But, in Go most ideal way to do so is:

import (
	"fmt"
	"time"
)

Pointer as Parameter

We will change the above example we pass pointer as parameter.

func checkIfAdult(p *Person) {
	t, err := time.Parse("2006-01-02", p.DOB)
	if err == nil {
		if t.Year() < 1995 {
			p.adult = true
		}
	}
}

Also change, call to the function checkIfAdult, in function main as below:

checkIfAdult(ptr)

Issue while using Pointers

, we have declared variable i as pointer of type int.  We pass this pointer variable to function assignValue. In function assignValue we create new variable j and initialize it to 1000 and update i to point to variable j.

func main() {
	var i *int
	assignValue(i)
	fmt.Println(i)
}

func assignValue(i *int) {
	j := 1000
	i = &j
}

http://play.golang.org/p/grLQI3uH_5

Output:

<nil>

Output is nil, because variable i is not initialized and when we pass it to function assignValue it is still nil.When in function assignValue, we assign value to variable i (i = &j), which then gets initialized to values of memory address to j, but only for function assignValue execution.

We can solve this, in two ways one by initializing variable i in main function or by returning from function assignValue and assigning it to i.

Approach 1: Initializing Value in main

func main() {

	a := 10
	var i *int = &a

	fmt.Println("Memory Address :", i)
	fmt.Println("Value at Memory Address :", *i)

	assignValue(i)

	fmt.Println("---------After assigning value-----------")
	fmt.Println("Memory Address :", i)
	fmt.Println("Value at Memory Address :", *i)

}

func assignValue(i *int) {
	j := 1000
	*i = j
}

http://play.golang.org/p/AAwGw-l4lP

Output:

Memory Address : 0x104382e0
Value at Memory Address : 10
———After assigning value———–
Memory Address : 0x104382e0
Value at Memory Address : 1000

In this approach, if you see the memory address before and after calling function assignValue is same, but value is updated from 10 to 1000. This is because of the call *i = j in function assignValue. This line updates value at the memory address pointed by i to the value of j

Approach 2: Returning Value

func main() {

	var i *int
	fmt.Println("Memory Address :", i)

	i = assignValue(i)

	fmt.Println("---------After assigning value-----------")
	fmt.Println("Memory Address :", i)
	fmt.Println("Value at Memory Address :", *i)
}

func assignValue(i *int) *int {
	j := 1000
	i = &j
	return i
}

http://play.golang.org/p/M0dtKSlMvT

Output:

Memory Address : <nil>
———After assigning value———–
Memory Address : 0x104382f0
Value at Memory Address : 1000

In this approach, since variable i is not initialized printing it we see it is nil, hence if call *i, it will throw error. Function assignValue here returns i back which is then assigned back. Check output after calling function assignValue, you can see memory address pointed and value as 1000.

Conclusion

Today’s post introduced you to Functions and Pointers in Go. They form the base of Go, and play very important part while developing applications in Go. In next post we will look at a very important concept in Go : interface, which form the base of OO design in Go language.

comments powered by Disqus