Web Analytics

Functions & Returns

Beginner ~35 min read

Functions are reusable blocks of code that perform specific tasks. In this lesson, you'll learn how to define functions, pass parameters, return values, and use Go's unique features like multiple return values and variadic parameters.

Function Basics

A function is declared using the func keyword:

Output
Click Run to execute your code
Function Syntax:
func functionName(param1 type1, param2 type2) returnType {
    // function body
    return value
}
  • func - keyword to declare a function
  • functionName - name of the function
  • parameters - input values (optional)
  • returnType - type of value returned (optional)

Functions Without Return Values

func greet(name string) {
    fmt.Printf("Hello, %s!\n", name)
}

func main() {
    greet("Alice")  // Hello, Alice!
}

Functions With Return Values

func add(a int, b int) int {
    return a + b
}

func main() {
    result := add(5, 3)
    fmt.Println(result)  // 8
}
Pro Tip: If consecutive parameters have the same type, you can omit the type for all but the last: func add(a, b int) int

Multiple Return Values

One of Go's most powerful features is the ability to return multiple values from a function:

Output
Click Run to execute your code
Multiple Returns Syntax:
func functionName(params) (type1, type2, type3) {
    return value1, value2, value3
}

Common Pattern: Returning Error

The most common use of multiple returns is returning a value and an error:

func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

func main() {
    result, err := divide(10, 2)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println("Result:", result)
}
Best Practice: In Go, errors are returned as values, not thrown as exceptions. Always check the error before using the result!

Named Return Values

You can name return values in the function signature. Named returns are automatically initialized to their zero values:

func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return  // "naked" return
}

func main() {
    a, b := split(17)
    fmt.Println(a, b)  // 7 10
}
Caution: Naked returns (return without values) can reduce readability in longer functions. Use them only in short functions.

Named Returns for Documentation

// Named returns make the function signature self-documenting
func getCoordinates() (x, y, z float64) {
    x = 10.5
    y = 20.3
    z = 5.7
    return
}

// vs unnamed returns (less clear)
func getCoordinates() (float64, float64, float64) {
    return 10.5, 20.3, 5.7
}

Variadic Functions

Variadic functions accept a variable number of arguments:

Output
Click Run to execute your code
Variadic Syntax:
func functionName(args ...type) {
    // args is a slice of type
}
  • The ... before the type makes it variadic
  • Inside the function, args is a slice
  • Must be the last parameter

Passing a Slice to Variadic Function

func sum(numbers ...int) int {
    total := 0
    for _, num := range numbers {
        total += num
    }
    return total
}

func main() {
    // Pass individual values
    fmt.Println(sum(1, 2, 3))  // 6
    
    // Pass a slice using ...
    nums := []int{1, 2, 3, 4, 5}
    fmt.Println(sum(nums...))  // 15
}

Function Parameters

Pass by Value

Go passes arguments by value (copies the value):

func modify(x int) {
    x = 100  // Only modifies the copy
}

func main() {
    num := 5
    modify(num)
    fmt.Println(num)  // Still 5
}

Pass by Reference (Using Pointers)

To modify the original value, pass a pointer:

func modify(x *int) {
    *x = 100  // Modifies the original
}

func main() {
    num := 5
    modify(&num)
    fmt.Println(num)  // Now 100
}

Functions as Values

Functions are first-class citizens in Goβ€”they can be assigned to variables:

func main() {
    // Assign function to variable
    add := func(a, b int) int {
        return a + b
    }
    
    result := add(5, 3)
    fmt.Println(result)  // 8
    
    // Pass function as argument
    operate(10, 5, add)
}

func operate(a, b int, op func(int, int) int) {
    result := op(a, b)
    fmt.Println("Result:", result)
}

Common Mistakes

1. Ignoring return values

// ❌ Wrong - ignoring error
result, _ := divide(10, 0)  // Don't ignore errors!
fmt.Println(result)

// βœ… Correct - check errors
result, err := divide(10, 0)
if err != nil {
    fmt.Println("Error:", err)
    return
}
fmt.Println(result)

2. Wrong number of return values

// ❌ Wrong - function returns 2 values
func getValues() (int, int) {
    return 5, 10
}

x := getValues()  // Error: multiple-value in single-value context

// βœ… Correct - capture both values
x, y := getValues()
// Or ignore one
x, _ := getValues()

3. Modifying parameters expecting change

// ❌ Wrong - won't modify original
func double(x int) {
    x = x * 2
}

num := 5
double(num)
fmt.Println(num)  // Still 5

// βœ… Correct - use pointer or return value
func double(x *int) {
    *x = *x * 2
}

num := 5
double(&num)
fmt.Println(num)  // 10

4. Variadic parameter not last

// ❌ Wrong - variadic must be last
func process(nums ...int, name string) {
    // Error!
}

// βœ… Correct - variadic parameter last
func process(name string, nums ...int) {
    // OK
}

Exercise: Statistics Calculator

Task: Create functions to calculate statistics for a set of numbers.

Requirements:

  • Create a variadic function average that calculates the mean
  • Create a function minMax that returns both minimum and maximum
  • Test with the numbers: 5, 2, 8, 1, 9, 3
  • Print average, min, and max
Show Solution
package main

import "fmt"

// Calculate average of numbers
func average(numbers ...float64) float64 {
    if len(numbers) == 0 {
        return 0
    }
    
    sum := 0.0
    for _, num := range numbers {
        sum += num
    }
    return sum / float64(len(numbers))
}

// Find minimum and maximum
func minMax(numbers ...float64) (min, max float64) {
    if len(numbers) == 0 {
        return 0, 0
    }
    
    min = numbers[0]
    max = numbers[0]
    
    for _, num := range numbers {
        if num < min {
            min = num
        }
        if num > max {
            max = num
        }
    }
    return
}

func main() {
    numbers := []float64{5, 2, 8, 1, 9, 3}
    
    // Calculate average
    avg := average(numbers...)
    fmt.Printf("Average: %.2f\n", avg)
    
    // Find min and max
    min, max := minMax(numbers...)
    fmt.Printf("Min: %.2f, Max: %.2f\n", min, max)
    
    // Bonus: Calculate range
    rangeValue := max - min
    fmt.Printf("Range: %.2f\n", rangeValue)
}

Summary

  • Functions are declared with the func keyword
  • Parameters are passed by value (copied)
  • Multiple return values are a key Go feature
  • Error handling uses return values, not exceptions
  • Named returns can improve readability
  • Variadic functions accept variable number of arguments
  • Functions are values and can be assigned to variables
  • Use pointers to modify original values

What's Next?

Now that you understand basic functions, you're ready to learn about Closures & Defer. In the next lesson, you'll discover anonymous functions, closures, and the defer statement for resource cleanup.