Skip to content
AgileLogics
6 min read

Multiple Return Values & Errors

Prerequisites: Functions

A Go function can return more than one value. The most common use is the value, err convention: a function that can fail returns its result and an error as the last value.

Go has no exceptions for ordinary failures. Instead, errors are plain values that you check right where they happen.

Syntax

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

result, err := divide(10, 2)
if err != nil {
    // handle the failure, usually by returning or reporting it
}
// use result

Example

package main

import (
    "errors"
    "fmt"
    "strconv"
)

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

func minMax(nums []int) (int, int) {
    lo, hi := nums[0], nums[0]
    for _, n := range nums {
        if n < lo {
            lo = n
        }
        if n > hi {
            hi = n
        }
    }
    return lo, hi
}

func main() {
    result, err := divide(10, 4)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Println(result)

    _, err = divide(1, 0)
    if err != nil {
        fmt.Println("Error:", err)
    }

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

    if _, err := strconv.Atoi("12a"); err != nil {
        fmt.Println("Error:", err)
    }
}

Expected output

2.5
Error: cannot divide by zero
1 9
Error: strconv.Atoi: parsing "12a": invalid syntax

Conventions to follow:

  • error is always the last return value.
  • When err is not nil, treat the other return values as meaningless (they're usually zero values).
  • Check the error immediately, and handle the failure first so the "happy path" continues unindented below.
  • Error messages start lowercase and don't end with punctuation, because they're often combined with other text.
  • fmt.Errorf("user %d not found", id) builds an error message with formatting — useful when you need to include values.

Wrapping errors, custom error types, and errors.Is/errors.As are covered in Level 2.

Common mistakes

  • Ignoring an error with _ "just for now" and forgetting about it. Only discard an error when you're sure it can't matter.
  • Using the result before checking err.
  • Returning a non-nil error and expecting callers to use the value too — pick one meaning.

Practice

Write parseAge(s string) (int, error) that converts s with strconv.Atoi, returns the conversion error if there is one, and returns the error age cannot be negative for values below zero. Call it with "30", "-4", and "abc".

Show solution
package main

import (
    "errors"
    "fmt"
    "strconv"
)

func parseAge(s string) (int, error) {
    age, err := strconv.Atoi(s)
    if err != nil {
        return 0, err
    }
    if age < 0 {
        return 0, errors.New("age cannot be negative")
    }
    return age, nil
}

func main() {
    for _, input := range []string{"30", "-4", "abc"} {
        age, err := parseAge(input)
        if err != nil {
            fmt.Println("Error:", err)
            continue
        }
        fmt.Println("Age:", age)
    }
}
Age: 30
Error: age cannot be negative
Error: strconv.Atoi: parsing "abc": invalid syntax