DEV Community

Cover image for Go Course: Functions
Karan Pratap Singh
Karan Pratap Singh

Posted on • Originally published at karanpratapsingh.com

Go Course: Functions

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() {}
Enter fullscreen mode Exit fullscreen mode

And we can call or execute it as follows.

...
myFunction()
...
Enter fullscreen mode Exit fullscreen mode

Let's pass some parameters to it.

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

func myFunction(p1 string) {
    fmt.Printtln(p1)
}
Enter fullscreen mode Exit fullscreen mode
$ go run main.go
Enter fullscreen mode Exit fullscreen mode

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) {}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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()
}
Enter fullscreen mode Exit fullscreen mode

We can also simplify this by making fn an anonymous function.

func myFunction() {
    func() {
        fmt.Println("inside fn")
    }()
}
Enter fullscreen mode Exit fullscreen mode

Notice how we execute it using the parenth*es*is 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
    }
}
Enter fullscreen mode Exit fullscreen mode
...
add := myFunction()

add(5)
fmt.Println(add(10))
...
Enter fullscreen mode Exit fullscreen mode

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
}
Enter fullscreen mode Exit fullscreen mode

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.

Init

In Go, init is a special lifecycle function which is executed prior to the main function.

Similar to main, the init function does not take any arguments nor returns any value. Let's see how it works with an example.

package main

import "fmt"

func init() {
    fmt.Println("Before main!")
}

func main() {
    fmt.Println("Running main")
}
Enter fullscreen mode Exit fullscreen mode

As expected, the init function was executed before the main function.

$ go run main.go
Before main!
Running main
Enter fullscreen mode Exit fullscreen mode

Unlike main, there can be more than one init function in a single or multiple files.

For multiple init in a single file, their processing is done in the order of their declaration, while init declared in multiple files are processed according to the lexicographic filename order.

package main

import "fmt"

func init() {
    fmt.Println("Before main!")
}

func init() {
    fmt.Println("Hello again?")
}

func main() {
    fmt.Println("Running main")
}
Enter fullscreen mode Exit fullscreen mode

And if we run this, we'll see the init functions were executed in order they were declared.

$ go run main.go
Before main!
Hello again?
Running main
Enter fullscreen mode Exit fullscreen mode

The init function is optional and is particularly used for any global setup which might be essential for our program, such as establishing a database connection, fetching configuration files, setting up environment variables, etc.

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...")
}
Enter fullscreen mode Exit fullscreen mode

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...")
}
Enter fullscreen mode Exit fullscreen mode
$ go run main.go
Doing some work...
Are you?
I am finished
Enter fullscreen mode Exit fullscreen mode

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.


This article is part of my open source Go Course available on Github.

GitHub logo karanpratapsingh / learn-go

Master the fundamentals and advanced features of the Go programming language

Learn Go

Hey, welcome to the course, and thanks for learning Go. I hope this course provides a great learning experience.

This course is also available on my website and as an ebook on leanpub. Please leave a ⭐ as motivation if this was helpful!

Table of contents

What is Go?

Go (also known as Golang) is a programming language developed at Google in 2007 and open-sourced in 2009.

It focuses on simplicity, reliability, and efficiency. It was designed to combine the efficacy, speed…

Top comments (0)