DEV Community 👩‍💻👨‍💻

nadirbasalamah
nadirbasalamah

Posted on

Golang tutorial - 6 Function

Writing a good code is a must in any programming language. Good code means the code runs effectively and efficiently. Not only that, a good code must be readable so the code is easy to understand by many people especially programmer or developer. Writing a good code can be done by splitting a big part of code into smaller chunks of code by using function.

Function in Go

Function is a piece of code to performs specific tasks that can be reused. In Go programming language. The anatomy of function declaration is look like this:

//func receiver functionName(parameters types) return type
 func (receiver)funcName(params) returnValues {
    //code
}
Enter fullscreen mode Exit fullscreen mode

To use the function, call the function name with arguments that specified in function.

//declare a function called greet
func greet() {
    fmt.Println("Hello!")
}

func main() {
    greet() //prints "Hello!"
}

Enter fullscreen mode Exit fullscreen mode

Based on the code above, we declare a simple function called greet() inside main function. We use term parameters in function declaration whilst we use term arguments in function usage. Here it is another example of function declaration.

//With one parameter with return value type string
func greet(s string) string {
    return fmt.Sprint("Hello ", s, "!")
}

//With two parameters with return value type boolean
func search(word string, keyword string) bool {
    return strings.Contains(word, keyword)
}

//With variadic parameter with two return values type int
func countAvg(xi ...int) (int, int) {
    total := 0
    //calculate sum of ints
    for _, v := range xi {
        total += v
    }
    //calculate average of int
    avg := total / len(xi)

    return total, avg
}
Enter fullscreen mode Exit fullscreen mode

These functions can be used inside main() function.

greetings := greet("John Doe")
sum, avg := countAvg(1, 2, 3, 4, 5, 6)
fmt.Println(greetings)
fmt.Println("sum: ", sum, " average: ", avg)
fmt.Println(search("Go is really awesome", "is"))
Enter fullscreen mode Exit fullscreen mode

Output:

Hello John Doe!
sum:  21  average:  3
true
Enter fullscreen mode Exit fullscreen mode

Based on the code above, there is an unique parameter called variadic parameters. Variadic parameter is a parameter that can be used by multiple values dynamically. Variadic parameter must be put in the last section of parameter declaration inside function in Go.

//example
func foo(s string, xi ...int) {
    //code
}
//this is incorrect, throw an error
func foo(xi ...int, s string) {
    //code
}
Enter fullscreen mode Exit fullscreen mode

Function with receiver

When declaring function in Go, the receiver in function can be declared as well. This receiver is really useful if we working with struct. The receiver determines the entity that capable to use a function.

Before writing a function, let's create a simple struct called person.

type person struct {
    name string
}
Enter fullscreen mode Exit fullscreen mode

Then, create a function called greet() with receiver type struct called person.

func (p person) greet() {
    fmt.Println("Hello, my name is", p.name, "!")
}
Enter fullscreen mode Exit fullscreen mode

To use this function, the struct must be instantiated first then call the function.

func main() {
    p := person{name: "Firstname"} //instantiate the struct
    p.greet()                      //call greet() from instantiated struct called p
}
Enter fullscreen mode Exit fullscreen mode

Output:

Hello, my name is Firstname !
Enter fullscreen mode Exit fullscreen mode

Anonymous Function

Anonymous function is a function without name or identifier. The declaration of anonymous function in Go is like this:

func() {
    //code
}
//using IIFE (Immediately-invoked Function Expression) style, we can declare a function that executes directly
func() {
    //code
}() //use this notation to execute the function 
Enter fullscreen mode Exit fullscreen mode

The usage of anonymous function can be seen in this example:

func() {
    //calculates sum
    xi := []int{1,2,3,4,5,6}
    total := 0
    for _, v := range xi {
        total += v
    }
    fmt.Println("The sum is: ",total)
}()
Enter fullscreen mode Exit fullscreen mode

Output:

The sum is:  21
Enter fullscreen mode Exit fullscreen mode

First Class Citizen in Go

The first class citizen principle in Go is also available but rarely to use. The first class citizen means that a function can be used as an argument or parameter in a function. Here it is the example.

func main() {
    //declare a function that stored inside variable called cb
    cb := func(xi ...int) int {
        total := 0
        for _, v := range xi {
            total += v
        }
        return total
    }

    //declare a slice of int
    data := []int{1, 2, 3, 4, 5, 6}
    //call foo() function with arguments:
    //1. cb means callback function
    //2. data... means retrieve all items from slice called "data"
    result := foo(cb, data...)
    fmt.Println("the result: ", result)
}

func foo(callback func(...int) int, xi ...int) int {
    return callback(xi...)
}
Enter fullscreen mode Exit fullscreen mode

Output:

the result:  21
Enter fullscreen mode Exit fullscreen mode

Closure

Closure means a function that could access variables from outside its body.
This is the example of closure.

//declare a function that returns a function with return value int
func increment() func() int {
    i := 0 //forms a closure
    return func() int { 
        i++ //access variable "i" from outside this function body
        return i
    }
}

func main() {
    myInt := increment()
    fmt.Println(myInt())
    fmt.Println(myInt())
    fmt.Println(myInt())

    myInt2 := increment() //this function call resets "i" value
    fmt.Println(myInt2())
}
Enter fullscreen mode Exit fullscreen mode

Output:

1
2
3
1
Enter fullscreen mode Exit fullscreen mode

Recursion

Recursion is a function that capable to executes a function itself. There are some problems that can be solved using recursion like factorial calculation. Here it is the example.

func factorial(n int) int {
    if n == 0 || n == 1 {
        return 1
    } else {
        return n * factorial(n-1) 
    }
}

func main() {
    result := factorial(5)
    fmt.Println("The result: ", result)
}
Enter fullscreen mode Exit fullscreen mode

Output:

The result:  120
Enter fullscreen mode Exit fullscreen mode

Based on the code above, the recursion function works like this:

the original formula of factorial:
5! = 5 * 4 * 3 * 2 * 1

using factorial function:
5! = factorial(5)
    = 5 * factorial(4)
    = 5 * 4 * factorial(3)
    = 5 * 4 * 3 * factorial(2)
    = 5 * 4 * 3 * 2 * factorial(1)
    = 5 * 4 * 3 * 2 * 1 * factorial(0)
    = 5 * 4 * 3 * 2 * 1 * 1
    = 120
Enter fullscreen mode Exit fullscreen mode

defer, panic and recover

There is a case when function needs to be executed at certain time for example functionA() has to be executed after certain function is finished. Based on this case, defer statement is really useful. defer basically pushes a function into a list. The list of a functions is executed after the surroundings function's execution is finished.

Here it is the example:

//(x,y int) is shorthand version of (x int, y int)
func calculate(x, y int) int {
    return x + y
}

func main() {
    defer fmt.Println("First")
    fmt.Println(calculate(4, 5))

}
Enter fullscreen mode Exit fullscreen mode

Output:

9
First
Enter fullscreen mode Exit fullscreen mode

Based on the code above, the deferred function fmt.Println("First") is executed after the fmt.Println(calculate(4, 5)) returns or finished.

There are many error handling mechanism in Go, one of the example is using panic function. If the error occurs, the panic is executed and the program is stopped.

The example of panic usage:

func divide(x, y int) int {
    if y == 0 { //if the value of y equals 0, then the program panicking and stopped
        panic("cannot divide by 0")
    } else {
        return x / y
    }
}

func main() {
    fmt.Println(divide(24, 0))
    fmt.Println("Hello") //this isn't executed because the divide() is panicking
}
Enter fullscreen mode Exit fullscreen mode

Output:

panic: cannot divide by 0
Enter fullscreen mode Exit fullscreen mode

Based on the code above, the panic function stops the program if the condition matches (in this case, if value of "y" equals to 0).

The deferred function is still executed altough the program is panicking.

func divide(x, y int) int {
    if y == 0 {
        panic("cannot divide by 0")
    } else {
        return x / y
    }
}

func main() {
    defer fmt.Println("Hello") //this is executed because this function is deferred
    fmt.Println(divide(24, 0))
}
Enter fullscreen mode Exit fullscreen mode

Output:

Hello
panic: cannot divide by 0
Enter fullscreen mode Exit fullscreen mode

To recover a program that is panicking, recover function can be used in deferred function. recover function become useless if used in non deferred function because if the program is panicking, the deferred function still executed.
Here it is the example:

func divide(x, y int) int {
    if y == 0 {
        panic("cannot divide by 0")
    } else {
        return x / y
    }
}

func main() {
    //declare an anonymous func to recover from panic
    defer func() {
        if r := recover(); r != nil {
            fmt.Println("Recovered!")
        }
    }()
    fmt.Println(divide(24, 0))
}
Enter fullscreen mode Exit fullscreen mode

Output:

Recovered!
Enter fullscreen mode Exit fullscreen mode

Based on the code above, the divide() function is panicking but the deferred function is still executed. The anonymous function is executed to recover from panicking then the program execution is stopped.

Notes

Learn more about function in Go:

I hope this article helpful for helping to learn the Go programming language. If you have any thoughts or feedbacks, you can write it in the discussion section below.

Top comments (0)

🌚 Friends don't let friends browse without dark mode.

Sorry, it's true.