>_
GolangStepByStep
Intern

Multiple Return Values

Return multiple values from a function — the Go idiom for results + errors

# Why Multiple Return Values Matter

Multiple return values are one of Go's core language features. They remove hidden control flow and make success/failure explicit at the call site.

Instead of exceptions, Go returns values like (result, error). This forces callers to handle errors right where the function is used, which makes code easier to reason about during debugging and incident response.

Real-World Analogyclick to expand

Think of a bank transaction API returning two envelopes: one with the result and one with a status note. You always inspect the status note first before trusting the result envelope.

That is exactly the pattern in Go: value, err := fn() thenif err != nil handle failure, otherwise continue.

# The Core Pattern: (result, error)

The most common function signature in Go returns a useful value plus an error.

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

result, err := divide(10, 2)
if err != nil {
    log.Fatal(err)
}
fmt.Println(result) // 5

# Step-by-Step: Caller-side Handling

1) Always capture both values

If a function returns two values, your assignment must receive two values. Example: v, err := parse().

2) Check error immediately

Keep error checks adjacent to call sites. Delayed checks make logic brittle and can leak invalid values deeper into execution.

3) Use _ deliberately

_ means “I intentionally ignore this value.” Use it only when ignoring is safe and documented by context.

# Named Return Values and Bare return

Named return values can improve readability for short functions and allow bare return. In long functions, prefer explicit returns for clarity.

func minMax(nums []int) (min, max int) {
    min, max = nums[0], nums[0]
    for _, n := range nums[1:] {
        if n < min {
            min = n
        }
        if n > max {
            max = n
        }
    }
    return // returns min, max
}

# Discarding Values with _

Use blank identifier only when you intentionally don't need one of the values.

// Discard error only when guaranteed safe
result, _ := divide(10, 2)
fmt.Println(result)

// Discard result, only care about error
_, err := os.Stat("/etc/hosts")
if os.IsNotExist(err) {
    fmt.Println("file not found")
}

# Real-World Example: Parse and Validate API Input

This pattern is common in request handlers: parse input, validate range, and return either valid value or explicit error.

package main

import (
    "fmt"
    "strconv"
)

func ParseAge(s string) (int, error) {
    age, err := strconv.Atoi(s)
    if err != nil {
        return 0, fmt.Errorf("invalid age %q: %w", s, err)
    }
    if age < 0 || age > 150 {
        return 0, fmt.Errorf("age out of range: %d", age)
    }
    return age, nil
}

func main() {
    age, err := ParseAge("25")
    fmt.Println(age, err)

    age, err = ParseAge("abc")
    fmt.Println(age, err)

    age, err = ParseAge("-5")
    fmt.Println(age, err)
}

⚡ Key Takeaways

  • Multiple returns make success and failure explicit at the call site
  • The standard Go shape is (value, error)
  • Check err immediately after each call
  • Use named returns mainly for short/documentation-focused functions
  • Use _ only when ignoring values is intentional and safe
practice & review