Multiple Return Values
Go has built-in support for multiple return values. This feature is used extensively in idiomatic Go, especially for returning both a result and an error value from a function.
Key Points
- Go supports returning multiple values from a single function
- The
(result, error)pattern is idiomatic for error handling - Use blank identifier
_to ignore return values you don't need - Named return values are initialized to zero values and can be modified before return
- Naked returns (return without arguments) use named return values
- Map lookups use
(value, ok)pattern where ok indicates if key exists - Type assertions use
(value, ok)pattern for safe type conversion - All return values must be handled or explicitly discarded
Basic Multiple Returns
A function can return multiple values by specifying them in parentheses:
package main
import "fmt"
// Function returns two integers
func vals() (int, int) {
return 3, 7
}
func main() {
// Get both return values
a, b := vals()
fmt.Println("vals:", a, b)
// Use only one return value, discard the other
_, c := vals()
fmt.Println("only second:", c)
}vals: 3 7
only second: 7Multiple Returns with Error Handling
The most common use of multiple returns is for error handling, where functions return a value and an error:
package main
import (
"fmt"
"errors"
)
// Function returns result and error
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("cannot divide by zero")
}
return a / b, nil
}
func main() {
// Successful operation
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("10 / 2 =", result)
}
// Error case
result, err = divide(10, 0)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}10 / 2 = 5
Error: cannot divide by zeroMultiple Return Patterns
| Pattern | Example | Use Case |
|---|---|---|
(T, error) | func read() ([]byte, error) | Most common: return value and error |
(T, bool) | func lookup() (string, bool) | Return value and success indicator |
(T1, T2) | func split() (int, int) | Return multiple related values |
(T1, T2, error) | func parse() (int, string, error) | Multiple values with error |
(x, y T) | func coords() (x, y int) | Named return values |
Named Return Values
| Feature | Description | Example |
|---|---|---|
| Declaration | Name return values in function signature | func split() (x, y int) |
| Initialization | Named returns are initialized to zero values | x and y start as 0 |
| Naked return | Return without arguments returns named values | return // returns x, y |
| Documentation | Makes return values self-documenting | func div() (quotient, remainder int) |
| Deferred functions | Can modify return values in defer | defer func() { x++ }() |
Best Practices
| Practice | Recommendation | Reason |
|---|---|---|
| Error as last return | Always return error as the last value | Idiomatic Go convention |
| Blank identifier | Use _ to ignore unwanted returns | Explicitly shows intention to discard value |
| Naked returns | Use sparingly, only in short functions | Can reduce readability in longer functions |
| Boolean vs Error | Use bool for existence, error for failures | Map lookups use bool; I/O operations use error |
| Return count | Limit to 2-3 returns when possible | Too many returns can indicate need for struct |