beginner
Step 4 of 15
Functions and Multiple Return Values
Go Programming
Functions and Multiple Return Values
Functions in Go are first-class citizens and have several distinctive features compared to other languages. Go functions can return multiple values, which is the standard way to handle errors. Named return values, variadic parameters, function types, and closures provide flexibility while maintaining Go's commitment to simplicity. Understanding Go's approach to functions — particularly the multiple-return-value pattern for error handling — is essential because it permeates every Go codebase.
Function Basics
package main
import (
"fmt"
"errors"
"math"
)
// Basic function
func add(a, b int) int {
return a + b
}
// Multiple return values
func divide(a, b float64) (float64, error) {
if b == 0 {
return 0, errors.New("division by zero")
}
return a / b, nil
}
// Named return values
func stats(numbers []float64) (min, max, avg float64) {
if len(numbers) == 0 {
return 0, 0, 0
}
min, max = numbers[0], numbers[0]
sum := 0.0
for _, n := range numbers {
if n < min { min = n }
if n > max { max = n }
sum += n
}
avg = sum / float64(len(numbers))
return // "naked return" — returns named values
}
// Variadic function
func sum(nums ...int) int {
total := 0
for _, n := range nums {
total += n
}
return total
}
func main() {
fmt.Println(add(3, 5)) // 8
result, err := divide(10, 3)
if err != nil {
fmt.Println("Error:", err)
} else {
fmt.Printf("Result: %.2f\n", result)
}
lo, hi, avg := stats([]float64{3, 1, 4, 1, 5, 9})
fmt.Printf("Min: %.1f, Max: %.1f, Avg: %.1f\n", lo, hi, avg)
fmt.Println(sum(1, 2, 3, 4, 5)) // 15
// Spread a slice into variadic function
nums := []int{10, 20, 30}
fmt.Println(sum(nums...)) // 60
}
Functions as Values and Closures
// Function type
type MathFunc func(float64, float64) float64
func applyOp(a, b float64, op MathFunc) float64 {
return op(a, b)
}
// Anonymous function
result := applyOp(10, 3, func(a, b float64) float64 {
return math.Pow(a, b)
})
// Closure
func makeCounter() func() int {
count := 0
return func() int {
count++
return count
}
}
counter := makeCounter()
fmt.Println(counter()) // 1
fmt.Println(counter()) // 2
fmt.Println(counter()) // 3
// Closure for middleware-like pattern
func withLogging(name string, fn func()) func() {
return func() {
fmt.Printf("[START] %s\n", name)
fn()
fmt.Printf("[END] %s\n", name)
}
}
task := withLogging("processData", func() {
fmt.Println("Processing...")
})
task()
Pro tip: Go's multiple return value pattern —result, err := someFunction()— is the idiomatic way to handle errors. Always check the error value immediately after the call. The blank identifier_can be used to explicitly ignore a return value:result, _ := someFunction(), but only do this when you are sure the error can be safely ignored.
Key Takeaways
- Go functions can return multiple values; the
(result, error)pattern is the standard for error handling. - Named return values document what a function returns and enable "naked returns."
- Variadic functions use
...Typeand can be called withslice...to spread elements. - Functions are first-class values — they can be assigned to variables, passed as arguments, and returned from functions.
- Closures capture variables from their enclosing scope, enabling patterns like counters and middleware.