Multiple return values are a distinctive feature of Go. They enable elegant error handling and returning multiple results without using pointers or structs. For more context, see Go Installation Guide, Go Ecosystem Overview, Go Best Practices.
Basic Multiple Returns
Returning Two Values
package main
import "fmt"
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, fmt.Errorf("division by zero")
}
return a / b, nil
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
Returning Multiple Values
package main
import "fmt"
func getCoordinates() (float64, float64, string) {
return 10.5, 20.3, "Point A"
}
func main() {
x, y, label := getCoordinates()
fmt.Printf("Point %s: (%.1f, %.1f)\n", label, x, y)
}
Named Return Values
Using Named Returns
package main
import "fmt"
func divide(a, b float64) (result float64, err error) {
if b == 0 {
err = fmt.Errorf("division by zero")
return
}
result = a / b
return
}
func main() {
result, err := divide(10, 2)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Result:", result)
}
}
Named Returns with Documentation
package main
import "fmt"
// Calculate returns the sum and product of two numbers
func calculate(a, b int) (sum int, product int) {
sum = a + b
product = a * b
return
}
func main() {
s, p := calculate(5, 3)
fmt.Printf("Sum: %d, Product: %d\n", s, p)
}
Error Handling Patterns
The Error Pattern
package main
import (
"fmt"
"os"
)
func readFile(filename string) ([]byte, error) {
data, err := os.ReadFile(filename)
if err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
return data, nil
}
func main() {
data, err := readFile("data.txt")
if err != nil {
fmt.Println("Error:", err)
return
}
fmt.Println("Data:", string(data))
}
Multiple Error Returns
package main
import (
"fmt"
"strconv"
)
func parseInts(strs []string) ([]int, error) {
result := make([]int, len(strs))
for i, s := range strs {
num, err := strconv.Atoi(s)
if err != nil {
return nil, fmt.Errorf("invalid number at index %d: %w", i, err)
}
result[i] = num
}
return result, nil
}
func main() {
nums, err := parseInts([]string{"1", "2", "3"})
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Println("Numbers:", nums)
}
}
Ignoring Return Values
Using Blank Identifier
package main
import (
"fmt"
"os"
)
func main() {
// Ignore error
data, _ := os.ReadFile("file.txt")
fmt.Println(string(data))
// Ignore data
_, err := os.ReadFile("file.txt")
if err != nil {
fmt.Println("Error:", err)
}
}
Returning Structs
Multiple Values as Struct
package main
import "fmt"
type Result struct {
Value int
Error error
}
func calculate(a, b int) Result {
if b == 0 {
return Result{
Value: 0,
Error: fmt.Errorf("division by zero"),
}
}
return Result{
Value: a / b,
Error: nil,
}
}
func main() {
result := calculate(10, 2)
if result.Error != nil {
fmt.Println("Error:", result.Error)
} else {
fmt.Println("Result:", result.Value)
}
}
Variadic Returns
Returning Slices
package main
import "fmt"
func getNumbers() []int {
return []int{1, 2, 3, 4, 5}
}
func main() {
nums := getNumbers()
fmt.Println(nums)
}
Best Practices
✅ Good: Always Return Errors
// DO: Return errors explicitly
func process(data string) (string, error) {
if data == "" {
return "", fmt.Errorf("data cannot be empty")
}
return strings.ToUpper(data), nil
}
❌ Bad: Ignore Errors
// DON'T: Ignore errors
func process(data string) string {
// No error handling
return strings.ToUpper(data)
}
✅ Good: Use Named Returns for Clarity
// DO: Use named returns for clarity
func divide(a, b float64) (result float64, err error) {
if b == 0 {
err = fmt.Errorf("division by zero")
return
}
result = a / b
return
}
✅ Good: Wrap Errors with Context
// DO: Wrap errors with context
if err != nil {
return fmt.Errorf("failed to process: %w", err)
}
❌ Bad: Generic Error Messages
// DON'T: Use generic error messages
if err != nil {
return fmt.Errorf("error")
}
Summary
Multiple return values provide:
- Error handling without exceptions
- Multiple results without structs
- Named returns for clarity
- Idiomatic Go patterns
- Explicit error propagation
These features make Go’s error handling explicit and intentional.
Comments