You have called functions since the first lesson: fmt.Println, strconv.Atoi. And you have written one in every program: main. This lesson shows how to write more functions of your own, and then puts everything from this unit into one complete program.
A function is a named step
A function is a block of statements with a name. Writing the function is called defining it. Running it is called calling it, and you can call it as many times as you like.
package main
import "fmt"
func greet() {
fmt.Println("Hello!")
fmt.Println("Welcome to the shop.")
}
func main() {
greet()
greet()
}This prints the two lines twice.
The definition starts with the word func, then the name, then round brackets, then the block in curly brackets. That is exactly the shape of main, which is a function like any other. Its one special property is that Go calls it for you when the program starts.
Three things to know:
- Functions are defined side by side, at the top level of the file. A function is never defined inside another function.
- Defining a function runs nothing. The program still starts at the first statement of
main. The lines ofgreetrun only when a statement calls it withgreet(). - The order of the definitions in the file does not matter.
greetcould be written belowmain.
Parameters let a function work on different values
A parameter is a variable that belongs to a function and receives a value each time the function is called. Parameters are listed in the round brackets of the definition, each with its type after its name.
package main
import "fmt"
func greet(name string) {
fmt.Println("Hello, " + name + "!")
}
func main() {
greet("Ada")
greet("Linus")
}This prints Hello, Ada! and Hello, Linus!.
You met the word argument in the first lesson: it is the value written between the round brackets of a call. Here "Ada" is an argument and name is the parameter that receives it. A function with two parameters separates them with a comma, func area(width int, height int), and is called with two arguments in the same order: area(3, 4).
return hands a value back
A function that prints is useful once. A function that hands a value back to the code that called it (its caller) is useful everywhere, because the caller decides what to do with the value. The statement return does this. The type of the value handed back is written after the round brackets of the definition.
package main
import "fmt"
func double(n int) int {
return n * 2
}
func main() {
result := double(4)
fmt.Println(result)
fmt.Println(double(10) + 1)
}This prints 8 and 21.
Read the first line of the definition as: "double takes one int, called n, and gives back an int". When Go reaches a call such as double(4), it runs the function with n set to 4, and the call is then replaced by the returned value. So result := double(4) becomes result := 8, and double(10) + 1 becomes 20 + 1.
return also ends the function at once. Any statements after it in the same block do not run.
Variables created inside a function, including its parameters, exist only while that call is running. n cannot be used inside main, and a variable of main cannot be used inside double. A function receives what it needs through its parameters and hands its answer back with return.
Print or return?
These two functions look alike and are very different:
package main
import "fmt"
func showDouble(n int) {
fmt.Println(n * 2)
}
func double(n int) int {
return n * 2
}
func main() {
showDouble(5)
total := double(5) + double(6)
fmt.Println(total)
}This prints 10 and 22. showDouble puts text on the screen and hands nothing back, so its definition has no type after the round brackets and its call cannot be used in arithmetic. double puts nothing on the screen; it produces a value that the caller can store, add, compare or print.
When a function's job is to work something out, return the result and let the caller print it.
Functions can contain everything you know
A function's block can hold variables, decisions and loops. Here a chain of if statements chooses the value to return:
package main
import "fmt"
func ticketPrice(age int) int {
if age < 5 {
return 0
} else if age < 16 {
return 6
} else {
return 10
}
}
func main() {
fmt.Println(ticketPrice(3))
fmt.Println(ticketPrice(9))
fmt.Println(ticketPrice(40))
}This prints 0, 6 and 10. Every path through the function ends in a return. Go insists on that for a function that promises a value.
You have already used a function that returns two values at once: strconv.Atoi hands back a number and an error value. Your own functions in this unit return one.
A complete program
Here is a small but complete interactive program: a times-table practice. It uses everything from the six lessons.
package main
import (
"bufio"
"fmt"
"os"
"strconv"
)
// verdict turns a score out of 3 into a short comment.
func verdict(score int) string {
if score == 3 {
return "Perfect!"
} else if score == 2 {
return "Nearly there."
} else {
return "Keep practising."
}
}
func main() {
scanner := bufio.NewScanner(os.Stdin)
fmt.Print("Which times table? ")
scanner.Scan()
table, err := strconv.Atoi(scanner.Text())
if err != nil {
fmt.Println("Please type a whole number.")
return
}
score := 0
for i := 1; i <= 3; i++ {
fmt.Printf("%d x %d = ", i, table)
scanner.Scan()
answer, err := strconv.Atoi(scanner.Text())
if err != nil {
fmt.Println("Please type a whole number.")
return
}
if answer == i*table {
fmt.Println("Correct.")
score++
} else {
fmt.Println("No, it is", i*table)
}
}
fmt.Printf("Score: %d out of 3\n", score)
fmt.Println(verdict(score))
}A session:
Which times table? 7
1 x 7 = 7
Correct.
2 x 7 = 14
Correct.
3 x 7 = 20
No, it is 21
Score: 2 out of 3
Nearly there.main and is called once, at the end.Read the program from main, not from the top of the file. verdict has one small job, a name that says what it does, and no printing of its own. That makes it easy to check by itself: verdict(3) must be Perfect! whatever the rest of the program is doing.
Try the program in the playground with four lines in the Standard input box.
Two errors you will meet
A path with no return.
package main
import "fmt"
func double(n int) int {
result := n * 2
fmt.Println(result)
}
func main() {
fmt.Println(double(4))
}# command-line-arguments
./main.go:8:1: missing returnThe first line of the definition promises an int, and the function reaches its closing curly bracket on line 8 without returning one. Printing a value is not returning it. End the function with return result.
An argument of the wrong type.
package main
import "fmt"
func double(n int) int {
return n * 2
}
func main() {
fmt.Println(double("4"))
}# command-line-arguments
./main.go:10:24: cannot use "4" (untyped string constant) as int value in argument to doubleThe parameter n is an int, and the call on line 10 passes the string "4". Read the message from its end: "in argument to double" tells you which call, "as int value" tells you what was needed, and the start tells you what was given. Pass a number, or convert the text first.
What you can do now, and where to go next
You can define a function, give it parameters, return a value from it, and decide whether a piece of code should print or return. You can read a program made of several functions starting from main. And you have now met every part of a complete interactive console program: output, variables, input, decisions, loops and functions.
The build for this lesson is the capstone of this unit: a number-guessing game that you write as one complete program.
When you have finished it, the next step is the next unit of this course, which starts at Go values, types & zero values. From there the course moves much faster than this unit: it will not explain again what a variable or a loop is, and its examples pack more into each line. Everything it relies on, you have now practised. Expect to read its lessons slowly and to run every example, and come back here whenever a foundation needs refreshing.