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:
erroris always the last return value.- When
erris notnil, 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