Introduction to Functions in Golang

A function is a block of code that takes some input(s), does some processing on the input(s) and produces some output(s).

Golang Functions Illustration

Functions help you divide your program into small reusable pieces of code. They improve the readability, maintainability, and testability of your program.

Declaring and Calling Functions in Golang

In Golang, we declare a function using the func keyword. A function has a name, a list of comma-separated input parameters along with their types, the result type(s), and a body.

Following is an example of a simple function called avg that takes two input parameters of type float64 and returns the average of the inputs. The result is also of type float64 -

func avg(x float64, y float64) float64 {
	return (x + y) / 2
}

Now, calling a function is very simple. You just need to pass the required number of parameters to the function like this -

avg(6.56, 13.44)

Here is a complete example -

package main
import "fmt"

func avg(x float64, y float64) float64 {
	return (x + y) / 2
}

func main() {
	x := 5.75
	y := 6.25

	result := avg(x, y)

	fmt.Printf("Average of %.2f and %.2f = %.2f\n", x, y, result)
}
# Output
Average of 5.75 and 6.25 = 6.00

Function parameters and return type(s) are optional

The input parameters and return type(s) are optional for a function. A function can be declared without any input and output.

The main() function is an example of such a function -

func main() {
}

Here is another example -

func sayHello() {
	fmt.Println("Hello, World")
}

You need to specify the type only once for multiple consecutive parameters of the same type

If a function has two or more consecutive parameters of the same type, then it suffices to specify the type only once for the last parameter of that type.

For example, we can declare the avg function that we saw in the previous section like this as well -

func avg(x, y float64) float64 { }
// Same as - func avg(x float64, y float64) float64 { }

Here is another example -

func printPersonDetails(firstName, lastName string, age int) { }
// Same as - func printPersonDetails(firstName string, lastName string, age int) { }

Functions with multiple return values

Go functions are capable of returning multiple values. That’s right! This is something that most programming languages don’t support natively. But Go is different.

Let’s say that you want to create a function that takes the previous price and the current price of a stock, and returns the amount by which the price has changed and the percentage of change.

Here is how you can implement such a function in Go -

func getStockPriceChange(prevPrice, currentPrice float64) (float64, float64) {
	change := currentPrice - prevPrice
	percentChange := (change / prevPrice) * 100
	return change, percentChange
}

Simple! isn’t it? You just need to specify the return types separated by comma inside parentheses, and then return multiple comma-separated values from the function.

Let’s see a complete example with the main() function -

package main
import (
	"fmt"
	"math"
)

func getStockPriceChange(prevPrice, currentPrice float64) (float64, float64) {
	change := currentPrice - prevPrice
	percentChange := (change / prevPrice) * 100
	return change, percentChange
}

func main() {
	prevStockPrice := 75000.0
	currentStockPrice := 100000.0

	change, percentChange := getStockPriceChange(prevStockPrice, currentStockPrice)

	if change < 0 {
		fmt.Printf("The Stock Price decreased by $%.2f which is %.2f%% of the prev price\n", math.Abs(change), math.Abs(percentChange))
	} else {
		fmt.Printf("The Stock Price increased by $%.2f which is %.2f%% of the prev price\n", change, percentChange)
	}
}
# Output
The Stock Price increased by $25000.00 which is 33.33% of the prev price

Returning an error value from a function

Multiple return values are often used in Golang to return an error from the function along with the result.

Let’s see an example - The getStockPriceChange function that we saw in the previous section will return ±Inf (Infinity) if the prevPrice is 0. If you want to return an error instead, you can do so by adding another return value of type error and return the error value like so -

func getStockPriceChangeWithError(prevPrice, currentPrice float64) (float64, float64, error) {
	if prevPrice == 0 {
		err := errors.New("Previous price cannot be zero")
		return 0, 0, err
	}
	change := currentPrice - prevPrice
	percentChange := (change / prevPrice) * 100
	return change, percentChange, nil
}

The error type is a built-in type in Golang. Go programs use error values to indicate an abnormal situation. Don’t worry if you don’t understand about errors for now. You’ll learn more about error handling in a future article.

Following is a complete example demonstrating the above concept with a main() function -

package main
import (
	"errors"
	"fmt"
	"math"
)

func getStockPriceChangeWithError(prevPrice, currentPrice float64) (float64, float64, error) {
	if prevPrice == 0 {
		err := errors.New("Previous price cannot be zero")
		return 0, 0, err
	}
	change := currentPrice - prevPrice
	percentChange := (change / prevPrice) * 100
	return change, percentChange, nil
}

func main() {
	prevStockPrice := 0.0
	currentStockPrice := 100000.0

	change, percentChange, err := getStockPriceChangeWithError(prevStockPrice, currentStockPrice)

	if err != nil {
		fmt.Println("Sorry! There was an error: ", err)
	} else {
		if change < 0 {
			fmt.Printf("The Stock Price decreased by $%.2f which is %.2f%% of the prev price\n", math.Abs(change), math.Abs(percentChange))
		} else {
			fmt.Printf("The Stock Price increased by $%.2f which is %.2f%% of the prev price\n", change, percentChange)
		}
	}
}
# Output
Sorry! There was an error:  Previous price cannot be zero

Functions with named return values

The return values of a function in Golang may be named. Named return values behave as if you defined them at the top of the function.

Let’s rewrite the getStockPriceChange function that we saw in the previous section with named return values -

// Function with named return values
func getNamedStockPriceChange(prevPrice, currentPrice float64) (change, percentChange float64) {
	change = currentPrice - prevPrice
	percentChange = (change / prevPrice) * 100
	return change, percentChange
}

Notice how we changed := (short declarations) with = (assignments) in the function body. This is because Go itself defines all the named return values and makes them available for use in the function. Since they are already defined, you can’t define them again using short declarations.

Named return values allow you to use the so-called Naked return (a return statement without any argument). When you specify a return statement without any argument, it returns the named return values by default. So you can write the above function like this as well -

// Function with named return values and naked return
func getNamedStockPriceChange(prevPrice, currentPrice float64) (change, percentChange float64) {
	change = currentPrice - prevPrice
	percentChange = (change / prevPrice) * 100
	return
}

Let’s use the above function in a complete example with the main() function and verify the output -

package main
import (
	"fmt"
	"math"
)

func getNamedStockPriceChange(prevPrice, currentPrice float64) (change, percentChange float64) {
	change = currentPrice - prevPrice
	percentChange = (change / prevPrice) * 100
	return
}

func main() {
	prevStockPrice := 100000.0
	currentStockPrice := 90000.0

	change, percentChange := getNamedStockPriceChange(prevStockPrice, currentStockPrice)

	if change < 0 {
		fmt.Printf("The Stock Price decreased by $%.2f which is %.2f%% of the prev price\n", math.Abs(change), math.Abs(percentChange))
	} else {
		fmt.Printf("The Stock Price increased by $%.2f which is %.2f%% of the prev price\n", change, percentChange)
	}
}
# Output
The Stock Price decreased by $10000.00 which is 10.00% of the prev price

Named return values improve the readability of your functions. Using meaningful names would let the consumers of your function know what the function will return just by looking at its signature.

The naked return statements are good for short functions. But don’t use them if your functions are long. They can harm the readability. You should explicitly specify the return values in longer functions.

Blank Identifier

Sometimes you may want to ignore some of the results from a function that returns multiple values.

For example, Let’s say that you’re using the getStockPriceChange function that we defined in the previous section, but you’re only interested in the amount of change, not the percentage change.

Now, you might just declare local variables and store all the values returned from the function like this -

change, percentChange := getStockPriceChange(prevStockPrice, currentStockPrice)

But in that case, you’ll be forced to use the percentChange variable because Go doesn’t allow creating variables that you never use.

So what’s the solution? Well, you can use a blank identifier instead -

change, _ := getStockPriceChange(prevStockPrice, currentStockPrice)

The blank identifier is used to tell Go that you don’t need this value. The following example demonstrates this concept -

package main

import (
	"fmt"
	"math"
)

func getStockPriceChange(prevPrice, currentPrice float64) (float64, float64) {
	change := currentPrice - prevPrice
	percentChange := (change / prevPrice) * 100
	return change, percentChange
}

func main() {
	prevStockPrice := 80000.0
	currentStockPrice := 120000.0

	change, _ := getStockPriceChange(prevStockPrice, currentStockPrice)

	if change < 0 {
		fmt.Printf("The Stock Price decreased by $%.2f\n", math.Abs(change))
	} else {
		fmt.Printf("The Stock Price increased by $%.2f\n", change)
	}
}
# Output
The Stock Price increased by $40000.00

Conclusion

That’s all folks! In this article, you learned how to declare and call functions in Golang, how to define functions with multiple return values and named return values, how to return an error from a function, and how to use a blank identifier.

Next Article: A beginners guide to Packages in Golang

Code Samples: github.com/callicoder/golang-tutorials