Functions in Go are first class citizens. Function may return one or more arguments. They may take a function as an argument. We cover these topics here. We take a look at variadic functions in Go, which are functions that take zero or more arguments. A deferred function is a function that is executed at the end of the execution of its enclosing function.

Functions may have multiple results, i.e. it might return more than one value separated by a comma. It is possible to have a name for each of the returned results for documentation purposes mostly.

func threeTimes(a, b string) (r1 string, r2 string, r3 string) {
	r := fmt.Sprintf("%s %s", a, b)
	return r, r, r
}

It is possible to omit the arguments of the return statement if the named results if they have been assigned within the function, but that is not recommended because it makes code harder to reason about.

func namedResult() (r string){
	r = "Hello Go"
	return
}

Go doesn’t support tail recursion optimization. However, function stacks in go is dynamic which mitigate the risk of stack over flow, thus most of reasonably bound or terminating recursion calls are safe.

Higher order functions

Higher order functions are functions that take functions as their argument. Since functions are first class citizens in Go, they have a type and they can be passed around. a function type is a representation for its arguments and its return. In this case we are passing behaviour as a parameter.

func consumeBehaviour(f func() string) {
	println(f())
}

consumeBehaviour(namedResult)

consumeBehaviour takes an argument f which takes no arguments and returns a string. The function newResult we defined earlier fits the requirement. We don’t even need to pass a concrete function, we can pass an anonymous function

consumeBehaviour(func () string {return "Hi"})

Variadic function

A variadic function can receive zero or more of its arguments. The significant thing to note here is the signature of the function func(...int), which means it doesn’t have the same signature as func([]int) which is a function taking a slice of int.

func variadic(x...int){
	println(fmt.Sprintf("The type of this function is: %T", variadic))              //  The type of this function is: func(...int)
	println(fmt.Sprintf("The type of argument is %T and its value is %v", x, x))    //  The type of argument is []int and its value is [1]
}

It is possible to pass a slice to a variadic function. We need to suffix the slice with ....

Defer

A function marked as defer is executed at the end of the enclosing function, regardless how the enclosing function ends. deferred functions are executed in the reverse order of their declaration inside the enclosing function. For example

func letsDefer(s string) (r string) {
	defer func() {println("defer 1:", s ,r)}()
	r = "r-" + s
	defer func() {println("defer 2:", s ,r)}()
	return r
}

will yield

defer 2: hello r-hello
defer 1: hello r-hello

Note that the second deferred function was executed first and both functions had the value of the result, even though it was not assigned at the time defer-1 was declared. A very intriguing use case is where one can capture a changing value at both the point of entry and exit of the function, to a more specific example, how about getting the running time of a function?

func functionAsFinally() {
	defer func() func() {
		before := time.Now()
		return func() { println("Elapsed time:", (time.Now().Sub(before)).String()) }
	}()()
}

In summary defer makes a function act like a finally block for the enclosing function.


Resources

  1. Ultimate Go Programming
  2. The Go Programming Language
  3. Go Documentation
  4. A tour of Go
  5. Go wiki: Switch
  6. The Go Programming Language Specification
  7. Go by Example