Kodewick
Start learning →
Lesson 5 of 24Contents

Functions

Lesson 5 of 24 · Part 1 · Getting started · about 15 minutes

When you need the same code in several places, wrap it in a function: give it a name and call it by that name. main is a function; here is another one:

prog.go
1package main2 3import "fmt"4 5func greet(name string) {6	fmt.Println("Hello, " + name)7}8 9func main() {10	greet("Lin")11	greet("Chen")12}
Output
Hello, Lin
Hello, Chen

func greet(name string) defines a function called greet. name is a parameter, and its type goes after the name: name string. greet("Lin") calls it; "Lin" is the argument, and inside the function it's name. The function can go before or after main.

Return values

A function can work out a value and hand it back: write the type of the result after the parentheses, and use return to give the value back to where it was called:

prog.go
1package main2 3import "fmt"4 5func total(price int, count int) int {6	return price * count7}8 9func main() {10	fmt.Println(total(25, 4))11	sum := total(12, 3) + total(8, 5)12	fmt.Println(sum)13}
Output
100
76

The call total(25, 4) is itself a value: you can show it, use it in a calculation, or store it in a variable. Parameters of the same type can share it: func total(price, count int) int.

A missing return

Once you say a function returns a string, every path through it needs a return. The Go compiler checks:

prog.go
1package main2 3import "fmt"4 5func grade(score int) string {6	if score >= 60 {7		return "Pass"8	}9}10 11func main() {12	fmt.Println(grade(72))13}
Output
# command-line-arguments
./prog.go:9:1: missing return

./prog.go:9:1: missing return points at line 9, the } that ends grade: when the score is under 60, the function gets there without returning anything. Add return "Retake" before that } and it works.

Several return values

A Go function can return several values at once. Put the result types in parentheses, separate the values after return with commas, and receive them with commas too:

prog.go
1package main2 3import "fmt"4 5func divmod(a, b int) (int, int) {6	return a / b, a % b7}8 9func main() {10	q, r := divmod(7, 3)11	fmt.Println(q, r)12	hours, minutes := divmod(135, 60)13	fmt.Println(hours, "hours", minutes, "minutes")14}
Output
2 1
2 hours 15 minutes

divmod gives the quotient and the remainder together: 135 minutes is 2 hours 15 minutes.

Dropping values with _

Sometimes you only want one of the values. But receive both and use only one, and you hit an old problem:

prog.go
1package main2 3import "fmt"4 5func divmod(a, b int) (int, int) {6	return a / b, a % b7}8 9func main() {10	hours, minutes := divmod(135, 60)11	fmt.Println(minutes, "minutes")12}
Output
# command-line-arguments
./prog.go:10:2: declared and not used: hours

hours is declared and not used, so it doesn't compile. Write _ for the value you don't want. It's a "throw away" slot, not a variable:

prog.go
1package main2 3import "fmt"4 5func split(total, people int) (each, left int) {6	each = total / people7	left = total % people8	return9}10 11func main() {12	each, left := split(100, 3)13	fmt.Println("Each:", each, "Left:", left)14	_, left = split(50, 4)15	fmt.Println("Left:", left)16}
Output
Each: 33 Left: 1
Left: 2

split also uses named results: (each, left int) names the two return values. They start at their zero values, you assign to them like ordinary variables, and a bare return at the end hands them back. It's fine now and then in a short function, but return value is usually clearer.

A function gets a copy

Guess first

What does this show?

1package main2 3import "fmt"4 5func double(n int) {6	n = n * 27}8 9func main() {10	price := 3011	double(price)12	fmt.Println(price)13}

When Go calls a function, it copies each argument's value into the parameter. This is called passing by value. Press Step and look at the right: inside addBonus a second level appears, with a score of its own. The two scores share a name but are two separate boxes.

prog.go
1package main2 3import "fmt"4 5func addBonus(score int) {6	score = score + 107	fmt.Println("In the function:", score)8}9 10func main() {11	score := 8012	addBonus(score)13	fmt.Println("In main:", score)14}
Output
In the function: 90
In main: 80

When the function returns, its level is gone. To get the new value into main, have the function return it and assign it: score = addBonus(score) (then addBonus has to return an int: func addBonus(score int) int, ending with return score). (The other way is a pointer, in lesson 11.)

Any number of arguments

fmt.Println takes one value, two, or many. Your functions can too: put ... before the type of the last parameter, and callers can pass as many values as they like, or none:

prog.go
1package main2 3import "fmt"4 5func sum(nums ...int) int {6	total := 07	for _, n := range nums {8		total += n9	}10	return total11}12 13func main() {14	fmt.Println(sum(12, 30, 8))15	fmt.Println(sum(99))16	fmt.Println(sum())17}
Output
50
99
0

Inside the function, nums is a slice holding those values (lesson 6 covers slices). for _, n := range nums takes them out one by one into n; the _ in front throws away their positions, which lesson 6 also explains.

Practice

Write the code, then press Check. A hint costs nothing; the answer is there when you need it.

Exercise 1

The price after a discount

Finish discount: take percent% off price and return the new price (a whole number). It should show 170 and 49.

It should print
170
49
exercise1.go
1package main2 3import "fmt"4 5func discount(price, percent int) int {6	// work it out here7	return 0 // a placeholder: replace it with the price you work out8}9 10func main() {11	fmt.Println(discount(200, 15))12	fmt.Println(discount(99, 50))13}
Output
Press Run or Ctrl+Enter
Exercise 2

Lowest and highest of three temperatures

Finish minMax so it returns both the smallest and the largest of three numbers. It should show Low: -3 High: 8 and Low: 15 High: 27.

It should print
Low: -3 High: 8
Low: 15 High: 27
exercise2.go
1package main2 3import "fmt"4 5func minMax(a, b, c int) (int, int) {6	return 0, 07}8 9func main() {10	low, high := minMax(5, -3, 8)11	fmt.Println("Low:", low, "High:", high)12	low, high = minMax(27, 15, 22)13	fmt.Println("Low:", low, "High:", high)14}
Output
Press Run or Ctrl+Enter
Exercise 3

Fix it: the tax that did not stick

addTax is meant to add 10% tax to a price, but price in main never changes. Fix it so it shows With tax: 220 and With tax: 60.

It should print
With tax: 220
With tax: 60
exercise3.go
1package main2 3import "fmt"4 5func addTax(price int) {6	price = price * 110 / 1007}8 9func main() {10	price := 20011	addTax(price)12	fmt.Println("With tax:", price)13	price = 5514	addTax(price)15	fmt.Println("With tax:", price)16}
Output
Press Run or Ctrl+Enter

What you learned

  • func name(param type) resultType { … }: types go after names, and parameters of the same type can share it, a, b int.
  • return hands a value back; a function with a result needs a return on every path, or it's a missing return error.
  • Several results: func divmod(a, b int) (int, int), received with q, r := divmod(7, 3); write _ for one you don't want.
  • Named results (each, left int) start at zero values, and a bare return hands them back.
  • Pass by value: a parameter is a copy of the argument, so changing it doesn't change the caller's variable; to get a new value, return it.
  • A variadic parameter nums ...int takes any number of values and is a slice inside the function.
Finished this lesson?Mark it. Progress is saved in this browser, and you can undo it.