Functions in Go

By Syncfusion®, Inc. · Last updated

Declare and call Go functions, pass parameters, return one, many or named values, and use variadic functions, closures, higher-order functions, recursion, defer and scope.

What Are Functions?

Function = a named group of steps that you can run again and again.

  • You write the steps once.
  • You call the function by its name whenever you need it.
  • You can give it values, and it can give a value back.

You have already used functions:

  • main() is a function you wrote.
  • fmt.Println() is a function from the fmt package.

Simple lesson: a function is a named recipe: write it once, use it many times.

A Real-World Example

Think of the school canteen's juice machine.

  • You put in fruit: that is the input.
  • The machine does the same steps every time: wash, cut, squeeze.
  • Out comes juice: that is the output.
  • You never need to know the steps. You just press the button.

Now the mapping:

  • The machine = a function.
  • The fruit = the parameters.
  • The juice = the return value.
  • Pressing the button = calling the function.

Functions make programs:

  • shorter, because steps are not repeated
  • easier to read, because each name explains a job
  • easier to fix, because a mistake is fixed in one place

What You Will Learn

In this lesson, you will learn:

  1. How to declare and call a function.
  2. How to pass one or more parameters.
  3. How to return one, many, or named values.
  4. Variadic functions, anonymous functions and closures.
  5. How to pass functions to other functions.
  6. Recursion, defer, and where variables can be seen (scope).

Function Fundamentals

Function Declaration

func name(parameters) returnType {
	// steps
}
  • func is the keyword that makes a function.
  • The name follows the naming rules from Variables and Constants.
  • Functions are declared outside main(), at the top level of the file.

Calling Functions

Call = run a function, by writing its name and ( ).

Declare once, call twice

package main

import "fmt"

func greet() {
	fmt.Println("Good morning, Green Valley!")
}

func main() {
	greet()
	greet()
}
Good morning, Green Valley!
Good morning, Green Valley!
  • Go starts at main().
  • Each greet() jumps into greet, runs its steps, then comes back.

Simple lesson: declare a function with func, then call it by name with ( ).

Parameters

Parameter = a variable in the function's brackets that receives a value.

Argument = the actual value you pass when you call it.

Single Parameters

One parameter

package main

import "fmt"

func welcome(name string) {
	fmt.Println("Welcome,", name)
}

func main() {
	welcome("Asha")
	welcome("Ravi")
}
Welcome, Asha
Welcome, Ravi
  • name string means: this function needs one string.
  • "Asha" is the argument for the first call.

Multiple Parameters

Two parameters

package main

import "fmt"

func report(name string, marks int) {
	fmt.Println(name, "scored", marks)
}

func area(length, width int) {
	fmt.Println("Area:", length*width)
}

func main() {
	report("Meena", 88)
	area(12, 8)
}
Meena scored 88
Area: 96
  • Separate parameters with commas.
  • length, width int is short for length int, width int.
  • Arguments go in the same order as the parameters.

Go Passes Copies

Go gives a function a copy of each argument. Changing the copy does not change the original.

The original does not change

package main

import "fmt"

func addBonus(marks int) {
	marks = marks + 10
	fmt.Println("Inside:", marks)
}

func main() {
	marks := 70
	addBonus(marks)
	fmt.Println("Outside:", marks)
}
Inside: 80
Outside: 70
  • To change the original, return the new value, or use a pointer. See Pointers.

Go keeps functions simple:

  • no default values for parameters
  • no two functions with the same name (no overloading)
  • every argument must be passed, in order

Simple lesson: parameters receive copies of the values you pass, in order.

Return Values

Return value = the value a function gives back with return.

Single Return Value

Return one value

package main

import "fmt"

func average(a, b, c int) float64 {
	return float64(a+b+c) / 3
}

func main() {
	avg := average(80, 90, 76)
	fmt.Println(avg)
}
82
  • float64 after the brackets is the return type.
  • return sends the value back and ends the function at once.

Multiple Return Values

Go functions can return more than one value.

Return two values

package main

import "fmt"

func divide(total, people int) (int, int) {
	return total / people, total % people
}

func main() {
	each, left := divide(17, 5)
	fmt.Println("Each gets", each, "laddoos,", left, "left over")
}
Each gets 3 laddoos, 2 left over
  • List the types in brackets: (int, int).
  • Receive them with two names: each, left := ....
  • Go uses this for errors: value, err := strconv.Atoi("42").

Named Return Values

Named return values = return values that have names, like variables.

Named return values

package main

import "fmt"

func minMax(a, b, c int) (smallest, biggest int) {
	smallest, biggest = a, a
	for _, n := range []int{b, c} {
		if n < smallest {
			smallest = n
		}
		if n > biggest {
			biggest = n
		}
	}
	return
}

func main() {
	low, high := minMax(72, 95, 64)
	fmt.Println(low, high)
}
64 95
  • smallest and biggest start at their zero values.
  • A bare return sends back their current values.
  • Good for short functions. In long ones, return smallest, biggest is clearer.

Simple lesson: return gives values back, and Go functions can return several at once.

Variadic Functions

Variadic function = a function that takes any number of arguments of one type.

Write ... before the type of the last parameter.

Add any number of marks

package main

import "fmt"

func total(marks ...int) int {
	sum := 0
	for _, m := range marks {
		sum += m
	}
	return sum
}

func main() {
	fmt.Println(total())
	fmt.Println(total(90, 85))
	fmt.Println(total(70, 80, 90, 100))

	list := []int{10, 20, 30}
	fmt.Println(total(list...))
}
0
175
340
60
  • Inside the function, marks is a slice: []int.
  • list... spreads a slice into separate arguments.
  • fmt.Println itself is variadic. That is why it takes any number of values.

Simple lesson: ...type lets a function take as many values as you like.

Anonymous Functions and Closures

Function Literals

Anonymous function = a function with no name. It is also called a function literal.

Functions without names

package main

import "fmt"

func main() {
	double := func(n int) int {
		return n * 2
	}
	fmt.Println(double(21))

	func(msg string) {
		fmt.Println(msg)
	}("Called right away")
}
42
Called right away
  • You can store a function in a variable, like double.
  • You can also call it at once by adding ( ) after the }.

Closures and Lexical Scope

Closure = a function that remembers the variables around it, even after the outer function has finished.

Lexical scope = a function can see the variables of the code that surrounds it.

A token counter that remembers

  • tokenMaker has finished, but next is not lost.
  • The returned function closes over next. That is why it is called a closure.
  • Each call to tokenMaker() makes a new, separate counter.

Simple lesson: a function literal is a function without a name, and a closure is one that remembers its surroundings.

Higher-Order Functions

Higher-order function = a function that takes a function, or returns one.

Passing Functions

The type of a function is written like func(int) int.

Pass a rule into a function

package main

import "fmt"

func applyToAll(marks []int, rule func(int) int) {
	for _, m := range marks {
		fmt.Print(rule(m), " ")
	}
	fmt.Println()
}

func main() {
	marks := []int{40, 65, 90}
	addGrace := func(m int) int { return m + 5 }
	outOfFifty := func(m int) int { return m / 2 }

	applyToAll(marks, addGrace)
	applyToAll(marks, outOfFifty)
}
45 70 95 
20 32 45 
  • applyToAll does not know the rule. It just calls it.
  • You choose the rule when you call it.

Returning Functions

A function that makes functions

package main

import "fmt"

func multiplier(k int) func(int) int {
	return func(x int) int {
		return x * k
	}
}

func main() {
	double := multiplier(2)
	triple := multiplier(3)
	fmt.Println(double(7), triple(7))
}
14 21
  • multiplier(2) builds a new function that remembers k = 2.
  • This is a closure too.

Simple lesson: functions are values: you can pass them in and return them.

Recursion

Recursion = a function that calls itself.

Base Case

Every recursive function needs two parts:

  • Base case = the simple case that stops the calls.
  • Recursive case = the function calls itself with a smaller problem.

Without a base case, the calls never stop.

Recursive Functions

Factorial of 4 = 4 × 3 × 2 × 1 = 24.

Press Play and watch the calls stack up, then come back down.

factorial(4), one call at a time

factorial with recursion

package main

import "fmt"

func factorial(n int) int {
	if n <= 1 {
		return 1
	}
	return n * factorial(n-1)
}

func main() {
	fmt.Println(factorial(4))
	fmt.Println(factorial(6))
}
24
720
  • Call stack = the pile of function calls waiting for an answer.
  • Each call waits on the stack until the call above it returns.
  • Too many calls (no base case) fill the stack, and the program crashes.

Simple lesson: a recursive function calls itself on a smaller problem, and the base case stops it.

Deferred Calls

defer

defer = run this call later, just before the function ends.

  • It is used for clean-up, like closing a file. See File Handling.
  • The deferred call runs even if the function ends early.

defer runs at the end

package main

import "fmt"

func main() {
	fmt.Println("Open the library")
	defer fmt.Println("Lock the library")
	fmt.Println("Issue books")
	fmt.Println("Collect returns")
}
Open the library
Issue books
Collect returns
Lock the library

Multiple defer Statements

Many deferred calls run in reverse order: last in, first out.

Think of a stack of plates. The last plate you put on top is the first one you take off.

defer order

  • The arguments of a deferred call are worked out when the defer line runs, not at the end.

Simple lesson: defer saves a call for the end of the function, and many defers run in reverse order.

Function Scope

Scope = the part of the code where a name can be used.

Local Scope

Local variable = a variable made inside a function or block. It exists only there.

Package Scope

Package-level variable = a variable made outside every function. All functions in the package can use it.

Local and package scope

package main

import "fmt"

var school = "Green Valley School"

func printHeader() {
	title := "Annual Report"
	fmt.Println(school, "-", title)
}

func main() {
	printHeader()
	count := 3
	if count > 0 {
		message := "Students present"
		fmt.Println(message, count)
	}
	fmt.Println(school)
}
Green Valley School - Annual Report
Students present 3
Green Valley School
  • school is package scope: both functions use it.
  • title lives only inside printHeader.
  • message lives only inside the if block.

Shadowing

Shadowing = an inner variable with the same name hides the outer one.

An inner variable hides the outer one

package main

import "fmt"

var level = "school"

func main() {
	level := "class"
	fmt.Println(level)
	{
		level := "bench"
		fmt.Println(level)
	}
	fmt.Println(level)
}
class
bench
class
  • Each := makes a new variable in a smaller scope.
  • Shadowing is legal, but it confuses readers. Use different names.

Easy way to remember

  • Made inside { } = lives inside those { }.
  • Made outside every function = the whole package can use it.
  • Keep variables in the smallest scope that works.

Simple lesson: a variable lives only inside the braces where it was made.

TL;DR

  1. Function = func name(params) returnType { }. Call it with name(args).
  2. Parameters = inputs. Go passes copies.
  3. Return values = return x. Go can return many: (int, error).

    • Named returns = (total int), then a bare return.
  4. Variadic = func sum(nums ...int). Spread a slice with list....
  5. Anonymous function = func(x int) int { ... }.

    • Closure = a function that remembers outer variables.
  6. Higher-order = takes or returns a function.
  7. Recursion = a function calls itself. Needs a base case.
  8. defer = run at the end, in reverse order.
  9. Scope = local (inside { }) or package (outside functions).

Common mistakes

MistakeWrong ideaCorrect ideaWhat goes wrong
A function inside main() with func name()Named functions can go anywhereNamed functions go at the top levelA syntax error. Use a function literal inside.
Forgetting return in a function with a return typeGo returns the last valueEvery path must returnmissing return
Changing a parameter to change the caller's variableParameters are the same boxParameters are copiesThe original does not change.
a, b := f() when f returns one valueYou can take as many as you likeTake exactly as many as it returnsassignment mismatch
Recursion with no base caseIt stops by itselfA base case must stop itThe stack overflows and the program crashes.
Expecting defer to run in written orderFirst defer runs firstLast defer runs firstClean-up happens in the wrong order.
Using a local variable in another functionVariables are sharedLocals live inside their bracesundefined: title

Check Yourself

Exercises

What to Remember

  • A function is a named recipe: func name(params) returnType { }.
  • Parameters get copies. Go functions can return many values.
  • ...type makes a function take any number of values.
  • Functions are values: you can store them, pass them and return them.
  • A closure remembers the variables around it.
  • Recursion needs a base case. defer runs at the end, in reverse order.
  • A variable lives inside the braces where it was made.

Next: Packages and Modules. You will learn how Go organises code into packages, and how modules manage a whole project.