Functions in Go

Learning objectives

Each algorithm that we encountered in the main text for solving some computational problem was represented using a function that takes a collection of parameters as input and that eventually returns some output solving the problem. That is, we have started reprogramming our brains to think about functions as the building blocks that we use to solve computational problems. Let us therefore learn a little about function syntax in Go.

Setup

Create a folder called functions in your go/src directory and create a text file called main.go in the go/src/functions folder. We will edit main.go in this lesson.

Code along video

Beneath the video, we provide a detailed summary of the topics and code covered in the code along.

At the bottom of this page, you will have the opportunity to validate your work via auto-graded assessments that evaluate the functions covered in the code along.

Although we strongly suggest completing the code along on your own, you can find completed code from the code along in our course code repository.

Code along summary

Our first function

As we declare variables, we declare functions. If we have a collection parameters of variables used as input to some function FunctionName(), then we can declare this function using the syntax func FunctionName(parameters), where each variable from parameters is specified to the function along with its type.

Our first function, SumTwoInts(), is shown below. This function takes two variables of type int as input and returns their sum. (This function is provided for the sake of simplicity; in practice, as we saw in the previous lesson, we can simply use the + operator to add integers.)

You can add the SumTwoInts() function anywhere you like in main.go as long as it does not occur inside func main() (or another function).

//SumTwoInts takes two integers and returns their sum.
func SumTwoInts(a int, b int) int {
    return a + b
}
Note: The line preceding SumTwoInts() contains a comment describing the function and specifying its input and what it returns. Function documentation comments are good programming practice (and you will see that they make you a stronger programmer).

The top line of our function, func SumTwoInts(a int, b int) int, is called the function signature. It specifies the name of the function, followed by each of two input parameters a and b, where each parameter is followed by its type. At the end of the function signature is the type (in this case int) of any variable(s) that the function returns. The rest of the function, where lines of code will be executed, is called the function body and must be enclosed in brackets ({}). Make sure to always remember your brackets!

SumTwoInts() takes more than one input variable, but functions can return more than one output variable as well. For example, consider the following function DoubleAndDuplicate() that takes a float64 variable x as input and returns two copies of 2.0*x. Note that we use the notation (float64, float64) at the end of the function signature to indicate that the function will be returning two values of type float64.

//DoubleAndDuplicate takes a float64 x as input.
//It returns two copies of 2x as output.
func DoubleAndDuplicate(x float64) (float64, float64) {
    return 2.0*x, 2.0*x
}

Although we typically think of functions as taking some input variable(s) and producing output value(s) solving some problem, functions do not need to take inputs or return anything. Here is a function Pi() that takes no inputs and returns a single (incorrect) value.

//Pi takes no inputs.
//It returns the value 3.14
func Pi() float64 {
    return 3.14 //wrong!
}

The following function prints Hi to the console, but it does not return a value.

//PrintHi simply prints the message "Hi" to the console.
func PrintHi() {
    fmt.Println("Hi")
}
Note: Beginner programmers often conflate the ideas of a function returning the value of a variable and a function printing the value of a variable. A variable returned by a function can be printed, but it can also be used as input to another function, as we saw in the main text with functions used as subroutines. In func PrintHi(), a statement is printed to the console, but the function does not return anything.

In fact, we have already seen a function that does not take any inputs or return any outputs, which is func main(). When you compile a file (using the terminal command go build) that has package main as its first line, Go knows to look for func main(), and it throws a compiler error if it does not find this function. When you run the executable file produced by the compiler, Go will then execute the lines of func main() in order.

Calling functions and ensuring that parameter type(s) match the function signature

Speaking of func main(), let’s return to func main() and show how that we can use our functions now that we have written them. Before continuing, use a multi-line comment to comment out any code that is present in func main().

Let’s call the SumTwoInts() function using two copies of an input variable x of type float64, as shown below. We will use a variable n of type int to store the output of this function, and then print the value of n to the console.

func main() {
    x := -2.3
    n := SumTwoInts(x, x)
    fmt.Println(n)
}

Compile this code using go build. Go provides a compiler error with the feedback cannot use x (type float64) as type int in argument to SumTwoInts. Just as in the previous lesson we saw that arithmetic operations often need to occur with variables having the same type, when we pass a variable as input into a function, then the variable must have the type indicated in the function signature. In this case, SumTwoInts() demands two parameters of type int, and Go therefore throws a compiler error when we attempt to call SumTwoInts() with an input of x (having type float64).

STOP: Instead of initializing x to have the value -2.3, change x to have the initial value 3. Now compile and run main.go, and you will see that 6 is printed to the console as we would hope.

Go uses pass by value for input parameters

Consider the function below, which adds one to the value of an input integer variable and then returns the variable.

//AddOne takes an integer k as input and returns the value of k+1.
func AddOne(k int) int {
    k = k+1
    return k
}

Consider the following func main().

func main() {
    m := 17
    fmt.Println(AddOne(m))
}    

When we compile and run our code, the final thing that we will see printed is 18, as we might expect. However, let’s see what happens when we also print m at the end of func main().

func main() {
    m := 17
    fmt.Println(AddOne(m))
    fmt.Println(m)
}    

You might imagine that the last line of this code will also print the value 18. But when we compile and run our code, we see that it instead prints 17! You can verify this with the following code.

When we call AddOne(m), a copy of m (called k) is created, and it is this copy that the function edits and then destroys at the end of the function body. When we run the above code, we see that k indeed receives the value of 18, but that m remains equal to 17.

Even if we rename the input parameter of AddOne() from k to m, as shown in the below code complete with comments, the same end behavior occurs.

//AddOne takes an integer m as input and returns the value of n+1.
func AddOne(m int) int { // the variable m gets created as a copy of whatever it is given.
    m = m+1
    return m // we are returning a value
    // the new m is now destroyed at the end of function
}

This example is part of a larger principle called pass by value (see figure below), which Go uses for many variable types, including strings, symbols, integers, and decimal numbers. Go does not use pass by value for all data types; we will see soon in the class that for some types, Go instead uses pass by reference, in which variables input to a function are not copied and any changes to these variables inside the function can affect the variables outside the function as well.

Figure: An illustration of pass by reference and pass by value using cups of coffee. In pass by reference, variables passed as input to a function can be changed by the function. In pass by value, which is used by Go for the types that we will work with in this chapter, input variables are copied, and any changes to these variables affect only the copies. Courtesy: penjee.com.

The order of functions does not matter

In a previous lesson, we noted that you can place as many lines of whitespace into your code as you like without any issue. A similar point is that you can place functions into a Go file in any order that you like, including func main(). Even if a function calls another function as a subroutine, the subroutine can occur before or after the function calling it.

This having been said, to avoid compiler errors, the package main declaration should occur first in your file, followed by any import statements needed, followed by your functions (in any order).

Functions with multiple inputs of the same type

Before continuing, we make a final note. As you start writing longer functions for more complicated problems, you may accumulate many parameters. If two or more input variables have the same type and occur consecutively in the input to a function, then Go allows a shorthand declaration that only specifies the type of these variables once, as illustrated by the following shortened SumTwoInts().

//SumTwoInts takes two integers and returns their sum.
func SumTwoInts(a, b int) int {
    return a + b
}

Now that we understand the basics of functions, we are now ready to continue exploring how Go implements the basics of control flow. In the next lesson, we will move on to discuss conditionals like if and else.

Check your work from the code along

Throughout this course, we will give you the opportunity to check your work via autograded code challenges. For example, after each code along, we provide short code challenges based on the functions covered in the code along so that you can ensure that the functions that you wrote are correct.

At the end of each chapter, we also include a collection of autograded exercises. All of these autograded assessments give you points, and exercises count for more than code along checks. If you earn enough points over all these challenges, you will earn a certificate of completion!

Our code assessments are hosted on a platform called Cogniterra. To get started, create an account at cogniterra.org and join our course at https://cogniterra.org/course/63. You can then log in to Cogniterra using the window below, and your progress will be automatically tracked across this course.

The following lesson allows you to check your SumTwoInts() and DoubleAndDuplicate() functions. It can also be found using a direct link. Beneath the window below, we provide a link to the next lesson.


close

Love P4❤️? Join us and help share our journey!

Page Contents
Scroll to Top