Functions

6 min read

Authors
banner

In this tutorial, we will discuss how we work with functions in Go. So, let's start with a simple function declaration

Simple declaration

func myFunction() {}

And we can call or execute it as follows

...
myFunction()
...

Let's pass some parameters to it

func main() {
	myFunction("Hello")
}

func myFunction(p1 string) {
	fmt.Printtln(p1)
}
$ go run main.go

As we can see it prints our message

We can also do a short hand declaration if the consecutive parameters have the same type. For example,

func myNextFunction(p1, p2 string) {}

Returning the value

Now let's also return a value

func main() {
	s := myFunction("Hello")
	fmt.Println(s)
}

func myFunction(p1 string) string {
	msg := fmt.Sprintf("%s function", p1)
	return msg
}

Multiple returns

Why return one value at a time, when we can do more? Go also supports multiple returns!

func main() {
	s, i := myFunction("Hello")
	fmt.Println(s, i)
}

func myFunction(p1 string) (string, int) {
	msg := fmt.Sprintf("%s function", p1)
	return msg, 10
}

Named returns

Another cool feature is named returns, where return values can be named and treated as their own variables

func myFunction(p1 string) (s string, i int) {
	s = fmt.Sprintf("%s function", p1)
	i = 10

	return
}

Notice how we added a return statement without any arguments, this is also known as naked return

I will say that, although this feature is interesting, please use it with care as this might reduce readability for larger functions

Functions as values

Next, let's talk about functions as values, in Go functions are first class and we can use them as values. So, let's clean up our function and try it out!

func myFunction() {
	fn := func() {
		fmt.Println("inside fn")
	}

	fn()
}

We can also simplify this by making fn an anonymous function

func myFunction() {
	func() {
		fmt.Println("inside fn")
	}()
}

Notice how we execute it using the parenthesis at the end

Closures

Why stop there? let's also return a function and hence create something called a closure. A simple definition can be that a closure is a function value that references variables from outside its body.

Closures are lexically scoped, which means functions can access the values in scope when defining the function.

func myFunction() func(int) int {
	sum := 0

	return func(v int) int {
		sum += v

		return sum
	}
}
...
add := myFunction()

add(5)
fmt.Println(add(10))
...

As we can see, we get a result of 15 as sum variable is bound to the function. This is a very powerful concept and definitely a must know.

Variadic Functions

Now let's look at variadic functions, which are functions that can take zero or multiple arguments using the ... ellipses operator.

An example here would be a function that can add a bunch of values

func main() {
	sum := add(1, 2, 3, 5)
	fmt.Println(sum)
}

func add(values ...int) int {
	sum := 0

	for _, v := range values {
		sum += v
	}

	return sum
}

Pretty cool huh? Also, don't worry about the range keyword, we will discuss it later in the course.

Fun fact: fmt.Println is a variadic function, that's how we were able to pass multiple values to it.

defer

Lastly, let's discuss the defer keyword, which lets us postpones the execution of a function until the surrounding function returns.

func main() {
	defer fmt.Println("I am finished")
	fmt.Println("Doing some work...")
}

Can we use multiple defer functions? Absolutely, this brings us to what is known as defer stack. Let's take a look at an example

func main() {
	defer fmt.Println("I am finished")
	defer fmt.Prinlnt("Are you?")

	fmt.Println("Doing some work...")
}
$ go run main.go
Doing some work...
Are you?
I am finished

As we can see, defer statements are stacked and executed in a last in first out manner.

So, Defer is incredibly useful and is commonly used for doing cleanup or error handling.

Functions can also be used with generics but we will discuss them later in the course.

So this is it for functions in Go, see you in the next tutorial.

© 2022 Shiva Poudel