Go by Example

Multiple Return Values

Go functions can return more than one value - the canonical pattern is returning a result alongside an error.

Go functions can return any number of values. The most common form is the (result, error) pair - a pattern that shows up in virtually every Go program and standard library call.

Declare multiple return types in parentheses. The caller receives all returned values and must handle each one - there is no implicit discard.

package main
 
import (
    "errors"
    "fmt"
)
 
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, 3)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    fmt.Printf("%.4f\n", result) // 3.3333
}

Use the blank identifier _ to discard a return value you don't need. This is idiomatic when you are certain an error cannot occur, or when you deliberately do not need the second value.

package main
 
import (
    "fmt"
    "strconv"
)
 
func minMax(nums []int) (int, int) {
    min, max := nums[0], nums[0]
    for _, n := range nums[1:] {
        if n < min {
            min = n
        }
        if n > max {
            max = n
        }
    }
    return min, max
}
 
func main() {
    nums := []int{3, 1, 4, 1, 5, 9, 2, 6}
    min, max := minMax(nums)
    fmt.Println(min, max) // 1 9
 
    // Discard the error when input is a known constant
    n, _ := strconv.Atoi("42")
    fmt.Println(n) // 42
}

Named return values give the return variables names in the signature. They are initialized to zero values and can be returned with a bare return. Use named returns for short functions where the names clarify intent.

package main
 
import "fmt"
 
// Named returns document what each value represents
func stats(nums []float64) (mean, variance float64) {
    n := float64(len(nums))
    for _, v := range nums {
        mean += v
    }
    mean /= n
    for _, v := range nums {
        d := v - mean
        variance += d * d
    }
    variance /= n
    return // naked return - only acceptable in short functions
}
 
func main() {
    m, v := stats([]float64{2, 4, 4, 4, 5, 5, 7, 9})
    fmt.Printf("mean=%.2f variance=%.2f\n", m, v) // mean=5.00 variance=4.00
}

In production

Returning (value, error) is the mechanism behind idiomatic Go error handling - it forces callers to decide what to do at every call site rather than letting errors propagate implicitly. Named return values are fine as documentation at the top of a short function, but naked returns in long functions hurt readability: during an incident you have to scroll back to the signature to understand what is being returned. Prefer explicit returns in all but the shortest helpers. Never use output parameters (pointer arguments as return slots) as a substitute for multiple returns - they obscure the function's contract and break the idiomatic call style.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free