# [Golang] Function deep-dive

2021-11-21

## Outline

In this blog post, I will introduce the details about `Function` more deeply and the various ways to use Function in Golang. You can see the full source code of this blog post on the link below.

## Function basic

If you want to know the basic of the function in Golang, see the previous blog post.

## Variable argument list function

In Golan,g you can use the variable argument list function like the below.

``````fmt.Println()
fmt.Println(1)
fmt.Println(1, 2, 3, 4, 5)
``````

You can make the variable argument list function with the `...` keyword in Golang.

``````func FUNCTION_NAME(param ...TYPE) TYPE {
// Code block
}
``````

To check this, create the `main.go` file and modify it like the below.

``````package main

import "fmt"

func sum(nums ...int) int {
fmt.Printf("nums type: %T\n", nums)

sum := 0
for _, num := range nums {
sum += num
}
return sum
}
func main() {
fmt.Println(sum(1, 2, 3, 4, 5))
fmt.Println(sum(10, 20))
fmt.Println(sum())
}
``````

When you run the above code, you can see the following result.

``````# go run main.go
nums type: []int
15
nums type: []int
30
nums type: []int
0
``````

## defer

Golang provides the `defer` keyword that is the delay execution statement. The `defer` ensures that the statement is executed before the end of the function.

``````defer STATEMENT
``````

To check this, modify the `main.go` file like the below.

``````package main

import "fmt"

func print() {
defer fmt.Println("World!")
fmt.Println("Hello")
}

func main() {
print()
}
``````

When the code is executed, you can seeh the following result.

``````# go run main.go
Hello
World!
``````

The `defer` is mainly used for returning the OS resource like closing the file that was opend to use it. To check this, modify the `main.go` file like the below.

``````package main

import "os"

f, err := os.Open("file.txt")

if err != nil {
return
}

f.Close()
}

f, err := os.Open("file.txt")
defer f.Close()

if err != nil {
return
}
}

func main() {
}
``````

If the `defer` is not used here and the the error occurs before calling the `Close` function, the `Close` function will not be called.

``````func readFile() {
f, err := os.Open("file.txt")

if err != nil {
return
}

f.Close()
}
``````

However, if the `defer` is used here, the `Close` function will be called.

``````func readFileDefer() {
f, err := os.Open("file.txt")
defer f.Close()

if err != nil {
return
}
}
``````

## Function type variable

In Golang, the function can be also used as the type. In here, Function type variable means the variables have the function as value. The function also has the memory address, so we can assign it to the variable.

You can define the function type by `Function signature` like the below.

``````// Function
func add(a, b int) int {
return a + b
}

// Signature
func (int int) int
``````

To check this, modify the `main.go` file like the below.

``````package main

import "fmt"

func add(a, b int) int {
return a + b
}

func multiple(a, b int) int {
return a * b
}

func getOperator(op string) func(int, int) int {
if op == "+" {
} else if op == "*" {
return multiple
} else {
return nil
}
}

func main() {
var op func(int, int) int

op = getOperator("+")
result := op(10, 20)
fmt.Println(result)

op = getOperator("*")
result = op(10, 20)
fmt.Println(result)
}
``````

When the code is executed, you can see the following result.

``````# go run main.go
30
200
``````

## Function signature alias type

You can use the function type more simpley with the alias type and the function signature.

``````// Function signature alias type
type OperateFunc func(int, int) int

func getOperator(op string) OperateFunc {
}
``````

To check this, modify the `main.go` file like the below.

``````package main

import "fmt"

type OperateFunc func(int, int) int

func add(a, b int) int {
return a + b
}

func multiple(a, b int) int {
return a * b
}

func getOperator(op string) OperateFunc {
if op == "+" {
} else if op == "*" {
return multiple
} else {
return nil
}
}

func main() {
var op OperateFunc

op = getOperator("+")
result := op(10, 20)
fmt.Println(result)

op = getOperator("*")
result = op(10, 20)
fmt.Println(result)
}
``````

When you execute the code, you can see the following result.

``````# go run main.go
30
200
``````

The alias type makes it easier to use the function signature.

## Function literal

In Golang, we can use the function literal(anonymous function).

``````f := func(a, b int) int {
}
``````

To check this, modify the `main.go` file like the below.

``````package main

import "fmt"

type OperateFunc func(int, int) int

func getOperator(op string) OperateFunc {
if op == "+" {
return func(a, b int) int {
return a + b
}
} else if op == "*" {
return func(a, b int) int {
return a * b
}
} else {
return nil
}
}

func main() {
var op OperateFunc

op = getOperator("+")
result := op(10, 20)
fmt.Println(result)

op = getOperator("*")
result = op(10, 20)
fmt.Println(result)
}
``````

When the code is executed, you can see the following result.

``````# go run main.go
30
200
``````

In Golang, the function can’t have the state normally, but the function literal can have the state. To check this, modify the `main.go` file like the below.

``````package main

import "fmt"

func main() {
i := 0

f := func() {
i += 10
}

i++

f()

fmt.Println(i)
}
``````

When you execute the code, you can see the following result.

``````# go run main.go
11
``````

Normal function can’t access the variables defined outside of the function. However, the function literal can access(capture) and use the variables defined outside of the function. The captured value is not copied value, but as a reference copy. In other words, it can be said that the pointer is copied.

To check this, modify the `main.go` file like the below.

``````package main

import "fmt"

func CaptureLoop1() {
f := make([]func(), 3)
fmt.Println("CaptureLoop1")

for i := 0; i < 3; i++ {
f[i] = func() {
fmt.Println(i)
}
}

for i := 0; i < 3; i++ {
f[i]()
}
}

func CaptureLoop2() {
f := make([]func(), 3)
fmt.Println("CaptureLoop2")

for i := 0; i < 3; i++ {
v := i
f[i] = func() {
fmt.Println(v)
}
}

for i := 0; i < 3; i++ {
f[i]()
}
}

func main() {
CaptureLoop1()
CaptureLoop2()
}
``````

When the code is executed, you can see the following result.

``````# go run main.go
CaptureLoop1
3
3
3
CaptureLoop2
0
1
2
``````

The `CaptureLoop1` refers to the `i` variable of the `for` directly, so 2 that is the last of `i` is printed. The `CaptureLoop2` refers to the `v` variable that is copied from `i` and the `v` is newly created every time in the for loop, so you can see 0~2 are printed.

## Dependency Injection

In Golang, you can use the function type for dependency injection(DI). Dependency injection is a technique that allows you to inject the business logic from the outside instead of defining it inside.

``````package main

import "fmt"

type PrintConsole func(string)

func PrintHello(p PrintConsole) {
p("Hello, World!")
}

func main() {
PrintHello(func(msg string) {
fmt.Println(msg)
})
}
``````

WHen the code is executed, you can see the following result.

``````# go run main.go
Hello, World!
``````

`PrintHello` doesn’t know what the role of `PrintConsole` is(what the business logic has). So, we can say `PrintHello` is injected dependency via `PrintConsole`.

## Completed

Done! we’ve seen the function deeply, and how to use it. Next, try to use the techniques that I introduced to make awsome codes.

` Was my blog helpful? Please leave a comment at the bottom. it will be a great help to me! `

## App promotion

You can use the applications that are created by this blog writer `Deku`.
`Deku` created the applications with Flutter.