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 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
}
}
...
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.
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")
}
As expected, the init
function was executed before the main
function.
$ go run main.go
Before main!
Running main
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")
}
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
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...")
}
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.
This article is part of my open source Go Course available on Github.
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
-
Getting Started
-
Chapter I
-
Chapter II
-
Chapter III
-
Chapter IV
-
Appendix
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)