Go functions natively support multiple return values — no tuple type or wrapper object required — which is how the idiomatic value, err := doThing() pattern works. The defer statement schedules a function call to run immediately before the enclosing function returns, regardless of which return statement fired or whether a panic occurred; multiple deferred calls run in last-in-first-out (LIFO) order.
Multiple return values
Any Go function can return more than one value, declared in the signature as a comma-separated list. This is the mechanism behind Go's error-handling convention: a function that can fail returns its normal result alongside an error, and the caller checks the error explicitly rather than catching an exception.
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("cannot divide %v by zero", a)
}
return a / b, nil
}
result, err := divide(10, 2)
if err != nil {
log.Fatal(err)
}
fmt.Println(result) // 5Named return values
A function's return values can be given names in the signature, which pre-declares them as local variables initialized to their zero value. A bare return with no arguments then returns whatever those named variables currently hold — useful for short functions but easy to make confusing in long ones, so idiomatic Go uses named returns sparingly.
func split(sum int) (x, y int) {
x = sum * 4 / 9
y = sum - x
return // returns x and y automatically
}defer: guaranteed cleanup, LIFO order
A defer call is scheduled to run right before its enclosing function returns — whether that's via a normal return, an early return, or a panic unwinding the stack. This makes it Go's idiomatic way to guarantee cleanup (closing a file, unlocking a mutex, closing a database connection) happens exactly once, right next to where the resource was acquired. Multiple defers in the same function run in last-in-first-out order: the most recently deferred call runs first.
func readFile(path string) error {
f, err := os.Open(path)
if err != nil {
return err
}
defer f.Close() // guaranteed to run when readFile returns, however it returns
// ... use f ...
return nil
}
func order() {
defer fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
}
// order() prints: 3, 2, 1 — LIFO