Multiple Return Values

A Go function can return more than one value. This simple feature shapes the whole language: instead of exceptions, functions return their result together with an error, and instead of out-parameters, they just return everything the caller needs. This lesson covers returning and receiving several values, the value, err convention, named results, and when to return a struct instead.

Returning several values

List the result types in parentheses, and return the values separated by commas:

GO
package main

import "fmt"

func minMax(nums []int) (int, int) {
	lo, hi := nums[0], nums[0]
	for _, n := range nums[1:] {
		lo = min(lo, n)
		hi = max(hi, n)
	}
	return lo, hi
}

func divmod(a, b int) (int, int) {
	return a / b, a % b
}

func main() {
	lo, hi := minMax([]int{7, 2, 9, 4})
	fmt.Println(lo, hi)

	q, r := divmod(17, 5)
	fmt.Printf("17 = 5*%d + %d\n", q, r)

	_, hi = minMax([]int{1, 50, 3}) // ignore one with _
	fmt.Println(hi)
}
Output
2 9
17 = 5*3 + 2
50

You must receive all the results or none: lo := minMax(…) doesn't compile. Use _ for the ones you don't need.

The value, error convention

The most common use of multiple results is returning a value together with an error. The error comes last; if it's nil, the operation succeeded and the value is meaningful. If it isn't, the caller should handle the error and usually ignore the value:

GO
package main

import (
	"errors"
	"fmt"
)

func averageOf(nums []float64) (float64, error) {
	if len(nums) == 0 {
		return 0, errors.New("no numbers to average")
	}
	sum := 0.0
	for _, n := range nums {
		sum += n
	}
	return sum / float64(len(nums)), nil
}

func main() {
	avg, err := averageOf([]float64{4, 8, 9})
	if err != nil {
		fmt.Println("error:", err)
		return
	}
	fmt.Println("average:", avg)

	if _, err := averageOf(nil); err != nil {
		fmt.Println("error:", err)
	}
}
Output
average: 7
error: no numbers to average

The pattern result, err := f(); if err != nil { … } appears in nearly every Go function. Lesson 44 covers errors in depth.

The comma-ok idiom

Another convention uses a bool as the second result to say whether the first is valid — like the map lookup you've already seen. It suits "not found" situations that aren't really errors:

GO
package main

import "fmt"

var capitals = map[string]string{"France": "Paris", "Japan": "Tokyo"}

func capital(country string) (string, bool) {
	c, ok := capitals[country]
	return c, ok
}

func main() {
	for _, country := range []string{"Japan", "Atlantis"} {
		if c, ok := capital(country); ok {
			fmt.Println(country, "->", c)
		} else {
			fmt.Println(country, "-> unknown")
		}
	}
}
Output
Japan -> Tokyo
Atlantis -> unknown

Passing results straight on

When a function's results match another function's parameters exactly, you can pass the call directly:

GO
package main

import "fmt"

func dimensions() (float64, float64) { return 3, 4 }

func area(w, h float64) float64 { return w * h }

func main() {
	fmt.Println(area(dimensions()))
	fmt.Println(dimensions()) // Println accepts any number of values
}
Output
12
3 4

Named results

Results can be given names. They start as zero values, document what each result means, and a bare return returns their current values:

GO
package main

import (
	"fmt"
	"strings"
)

func splitName(full string) (first, last string) {
	parts := strings.Fields(full)
	if len(parts) == 0 {
		return // returns "", ""
	}
	first = parts[0]
	if len(parts) > 1 {
		last = parts[len(parts)-1]
	}
	return
}

func main() {
	f, l := splitName("Grace Brewster Hopper")
	fmt.Printf("%q %q\n", f, l)
	f, l = splitName("Plato")
	fmt.Printf("%q %q\n", f, l)
}
Output
"Grace" "Hopper"
"Plato" ""

Named results are most useful as documentation when a function returns two values of the same type, such as (first, last string). Bare returns save typing but make longer functions harder to read, because you have to look elsewhere to see what's returned — use them only in short functions. Named results also let a deferred function change what's returned (lesson 34).

When to return a struct instead

Two or three results are fine. Beyond that, callers have to remember the order and it's easy to mix up values of the same type. Group related results into a struct (lesson 36):

GO
package main

import "fmt"

type Stats struct {
	Count         int
	Min, Max, Sum float64
}

func describe(nums []float64) Stats {
	s := Stats{Count: len(nums), Min: nums[0], Max: nums[0]}
	for _, n := range nums {
		s.Min, s.Max, s.Sum = min(s.Min, n), max(s.Max, n), s.Sum+n
	}
	return s
}

func main() {
	st := describe([]float64{3.5, 9, 1.25})
	fmt.Printf("%+v\n", st)
	fmt.Println("mean:", st.Sum/float64(st.Count))
}
Output
{Count:3 Min:1.25 Max:9 Sum:13.75}
mean: 4.583333333333333

Try it yourself

Write a function that parses "width x height" text such as "1920x1080" and returns both numbers and an error.

Try it yourself · main.go
Edit the code, copy it into main.go in a new folder and run it with go run main.go (Go 1.27 or later). How to install Go
Output
1920x1080 -> 1920 1080 <nil>
800-600 -> 0 0 missing 'x'
64xabc -> 0 0 strconv.Atoi: parsing "abc": invalid syntax
Next: functions that take any number of arguments.