Functions are an important aspect of any programming language in that they allow us to create reusable blocks of code that can use a variety of input values. You've used functions in previous lessons, including the functions main
and Println
. In this lesson, you will take a closer look at functions and learn to create your own.
A function is a block of organized, reusable code that uses one or more Go statements to complete a single, related action. You use functions to help with code reusability, increased readability, and redundancy checking (making sure that you do not use the same lines over and over within an application).
Go provides many built-in functions, such as the fmt.Println()
function that you've been using throughout this book; however, you are also allowed to create or define your own functions. A function that you define yourself in a Go program is known as a user-defined function.
As you progress through this lesson, here are a few key terms you should know:
def
keyword to define them.Functions allow you to organize snippets of code that can then be used (called) to perform specific functionality. One of the core values of functions is that you can write the code once and then call it as many times as you would like.
You define a function using the keyword func
, and you can define any values you need for the function as parameters of that function. The basic syntax to define a function is:
func funcName (arg1 type, arg2 type) returnType {
[function instructions]
}
A function can take zero or more parameters, depending on what you want it to do, so it is possible to define a function without any parameters at all, as in main()
. Let's look at a simple function that adds two numbers. A function called add
is presented in Listing 8.1.
There are two areas of Listing 8.1 that are worth a close look. First is the definition of the add
function. You see this definition starting with the func
keyword, followed by the name of the new function being defined, add
. You then see that the function has two parameters called a
and b
that are both defined as type int
. At the end of the function declaration line, you see that the type of int
is listed, which means that the add
function is expecting to return an integer value.
The body of the add
function is contained between a set of brackets ({}
). In the case of the add
function, the body is only a single line of code:
return a + b
This simple line of code adds the values that are in a
and b
and returns the result. Note that a
and b
are parameters, so they are expected to be provided when the add
function is called. Similarly, the return
statement returns the result of the addition to the code that called the function.
In fact, you can see that the add
function is called from within the Println
function that is in the main
function of the listing. As expected, you can also see that the values of 4 and 6 are passed as arguments to the function. The returned value from the call to add
is then used within the Println
statement. The resulting output is:
add function results: 10
Note that the add
function is created outside the main
function. This makes it a global function that can be accessed from inside the main
function or from anywhere else within our program listing.
A program can include multiple functions, and you can use the same values in multiple functions. Listing 8.2 is a variation of the previous program, with a simplified add2
function and a new multiply
function.
When you execute this program, the following output is displayed:
c = 5
d = 6
add result: 11
add2 result: 11
multiply result: 30
In this case, the add2
function has a simpler signature than the original add
function. Specifically, you use (a, b int)
to define the function's parameters. You can do this only because both parameters are of the same type. If the function used parameters of different types, you would have to use the syntax in the add
function.
You also use variables instead of hard-coded values in the main
function. This allows you to easily reuse the numbers in multiple functions. Here, you used the values of c
and d
in each of the functions.
In the functions created in the previous two listings, you returned a single calculated value. You don't have to return a value in a function, especially if the function provides output already.
In Listing 8.3, the function converts a string to uppercase and prints the results as part of the function. Note that the listing includes the package called strings
, which contains functions you'll use in the new function being created.
In this listing a new function called DisplayUpper
is created that takes a string called x
. You can see that there is no return type listed after the parameter, so nothing is expected to be returned. The function itself simply prints the original text, then uses the ToUpper
function in the strings
package to convert the string to uppercase. When this listing is executed, the following output should be displayed:
Original text: elizabeth
Revised text: ELIZABETH
You can change the value of a
from elizabeth
to any other string and run the program again. You can also pass a string literal to our new function to convert it as well.
You've now seen how to return a single value from a function as well as how to create a function that does not return a value. You can also create functions that return multiple values.
To return a single value, you include the returned data type in the function declaration:
func funcName (arg1 type, arg2 type) returnType {
In this case, returnType
is the type of the data to be returned. To return more than one value, you can place each of the return types in parentheses separated by a comma:
func funcName (arg1 type, arg2 type) (returnType, …, returnType) {
The created function will then need to return all the values with the return
statement. Each value should be separated by a comma, as shown in Listing 8.4.
While this is not the best application of using a function, it provides a simple illustration of returning multiple values. The function rectStuff
is declared to receive two arguments (length
and width
). More importantly, it also returns two integer values. Looking within the function, you can see that two variables are declared and assigned values determined based on what was passed to the function. Calculations are performed and the results are assigned to the variables a
and c
. A single return
statement then returns both of these values back to the calling function.
Looking in the main
function, you can see that the rectStuff
function is called with the two arguments of 3
and 5
. To the left of the assignment operator, you see that there are two variables separated by a comma that are ready to receive the two integer values that will be returned.
When this listing is executed, the following output should be displayed:
area: 15
perimeter: 16
When returning multiple values from a function, it is not mandatory that they be the same. In Listing 8.4, both data types were of type int
. You could use other data types as well. The important thing is that you must return values of the types you indicate. Listing 8.5 presents a function that returns two different data types.
This time, instead of determining characteristics of a rectangle, the listing determines a diameter and circumference of a circle. Looking at the circleStuff
function details, you can see that a value of type int
and a value of type float32
are returned, in that order. The function itself simply calculates the diameter (d
) and the circumference (c
) and then returns the results in a single return
statement. You should note that because you are calculating a floating-point number using an integer, you do need to use a cast function on the radius to avoid a type mismatch error. The results of running this listing when passing a radius of 5 are:
Diameter: 10
Circumference: 31.400002
In Go you can also define the names of the return values. In Listing 8.6, the circleStuff
function has been rewritten to use named return values.
When the return values are named, those names are then usable within the function. You can see in the listing that the circleStuff
function returns two values, one of type int
and one of type float32
. More importantly, you can see that each return value is given a name prior to the type. The return variable d
is defined as the int
, and the return variable c
is defined as the float32
. You are also using math.Pi
instead of 3.14, so your output will be more accurate.
Within the code, values are assigned to these return variables. Because the return variables have already been identified, there is no need to list them after the return
statement. The output from running Listing 8.6 is similar to the previous listing:
Diameter: 10
Circumference: 31.415928
When calling a function that returns multiple values, you have the option to skip the use of some of the values. In fact, you can use the blank identifier (_) to indicate you do not need a value returned. Listing 8.7 is a modification of Listing 8.6. In this case, only the diameter is needed, so the circumference is skipped.
When you run this listing, the output is
diameter: 10
Notice that within the main
function, the blank identifier was used instead of a variable for circumference. This allowed the diameter to be retrieved without getting the circumference.
You might wonder why you don't simply include a placeholder variable when you call the listing. The reason is because Go will give an error if you declare a variable and don't use it.
In general, functions are designed to accept a fixed number of parameters, but there are cases where you may not know ahead of time how many parameters you need, especially if you are working with data structures like arrays and maps that can be of different lengths. When you're using a variadic function (a function with a variable number of parameters), all parameter values must be of the same type. You define the parameter using this syntax:
func funcName (parameterName … type) [returnType] {
// function instructions
}
Listing 8.8 presents a function called sumN
that accepts a variable number of inputs. The function adds the values of all the arguments received and returns the sum.
The output is
Current element: 4 ; Current index: 0
Current element: 6 ; Current index: 1
Current element: 5 ; Current index: 2
Sum of values: 15
Current element: 4 ; Current index: 0
Current element: 6 ; Current index: 1
Current element: 5 ; Current index: 2
Current element: 6 ; Current index: 3
Current element: 7 ; Current index: 4
Current element: 8 ; Current index: 5
Sum of values: 36
In this example, the sumN
function accepts a range of values rather than a fixed number of values, and it returns no value. It includes a for
loop that uses range
to iterate through the values provided, adding each value to the current total, printing each value and its index number as it cycles through the loop.
Finally, it prints the sum of the numbers provided when the function is called. In the first case, it has only three numbers to work with, but in the second case, it has six numbers.
Go also supports recursive functions. A recursive function is one that calls itself. Listing 8.9 shows one of the most common uses of recursion, which is calculating the factorial of a given number.
In this example, you create a function named factorial
, and this function calls itself in the process of calculating the factorial of a given number. Looking closer, you can see that the function receives an integer. If that integer is 0, then the value of 1 is returned. If the number passed to the function is not 0, then the function is called again when the return
statement is executed.
The main
function passes the value of 5 to the factorial function. The resulting output is the number 120.
In Go, you can assign a function to a variable and then reference the variable when the function is needed. In Listing 8.10, you use the circleStuff
function described earlier, but this time, instead of defining the function outside of the main
function, you create a variable named circleStuff
and assign the function to that variable.
This program works identically to the earlier version, with exactly the same output:
10 31.415928
Once you define the variable, you can use it to reference the function anywhere in the main
function.
Go supports anonymous functions. An anonymous function is a function that does not have a name. In some cases, you simply want the function to run once as part of the main program; you don't need to name it because you don't plan to reuse it. Alternatively, you can assign the anonymous function to a variable or use it as part of a larger function and then call the function through the variable or parent function instead of calling it directly.
A closure is a special type of anonymous function that references variables declared outside of the function itself. In a normal function, you use either constants or variables in the main program and pass those values to the parameters defined in the function. The function itself uses its own parameters as the variables.
In a closure, however, you simply reuse the variables you initialized elsewhere and call them directly in the closure. As an example (see Listing 8.11), let's go back to the simple add
function you used earlier and use it as an anonymous function.
In this program, you create an anonymous function that adds two values, and you assign the function to a variable. The function itself is a closure because it calls the variables defined in the main
function, and it does not have any defined parameters of its own.
You use the variable add
to call the function. Because the function already knows what values to use, you do not need to provide those values when you call the function.
Listing 8.12 includes a closure as part of the passGenerator
function.
The passGenerator
function is configured to generate a password using a series of randomly generated characters. The resulting password is saved to the pwd
variable, a variable that is defined outside of the anonymous function that generates the password.
In this lesson you learned about one of the important features for organizing your code. As you write programs using Go, it is important to organize functionality. You can do so with functions.
Not only did you learn how to create your own functions, but you also learned how to pass items to a function in a variety of ways as well as how to return and receive information from the functions you create. The lesson ended by covering a couple of more advanced topics, including recursion, assigning a function to a variable, and closures.
In the next lesson, you'll start to dig into special data structures that can be used to hold information. You will be learning about arrays!
The following exercises are provided to allow you to experiment with the tools and concepts presented in this lesson. For each exercise, write a program that meets the specified requirements and verify that the program runs as expected. The exercises are:
Write a program that uses at least two customized functions to perform operations on at least two values input by the user. As an example, you could create a function that compares an input value to a fixed value to determine if the values are the same. As a more complicated approach, you could create a login function that compares an input username and password to a known username and password and displays appropriate feedback based on whether or not both input values match the known values.
Rename the circleStuff
function presented in the lesson to sphereStuff
. Modify the function to also return the area of the sphere in addition to the circumference and diameter.
Create a new function called petSound
. The function should take the name of a pet as the parameter. The function should return a string that indicates the sound the pet makes.
For example, if the function were called with dog
in the following manner:
fmt.Println("A dog says", petSound("dog"))
then, the return value would be woof
and the output would be
A dog says woof
Research other problems that can use recursion to find the solution. Write a program that includes a recursive function for at least two other examples.
Implement a Fibonacci function that returns a function (a closure) that returns successive Fibonacci numbers (0, 1, 1, 2, 3, 5, …).
Create a calculator application that runs from the command line. The calculator should perform the following tasks:
quit
or exit
at any prompt to end the program.8 + 9 = 17
Additional requirements: