Go Functions

Go functions support multiple return values, closures, variadic arguments, and named returns. They are first-class values — you can pass them as arguments and store them in variables.

Multiple Return Values

This is Go's most distinctive function feature:

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

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

Convention: the last return value is always the error.

Named Return Values

Go
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return // bare return — returns x and y
}

Named returns are rarely used in modern Go — prefer explicit returns.

Variadic Functions

Accept any number of arguments:

Go
func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

sum(1, 2, 3)     // 6
sum(1, 2, 3, 4)  // 10

// Pass a slice
nums := []int{1, 2, 3}
sum(nums...)  // 6

Functions as Values

Go
// Assign to a variable
add := func(a, b int) int {
    return a + b
}
fmt.Println(add(3, 4)) // 7

// Pass as an argument
numbers := []int{1, 2, 3, 4, 5}
evens := filter(numbers, func(n int) bool {
    return n%2 == 0
})

Defer

defer runs when the surrounding function returns — perfect for cleanup:

Go
func readFile(path string) error {
    f, err := os.Open(path)
    if err != nil {
        return err
    }
    defer f.Close()  // guaranteed to run, even if an error occurs

    // ... read from f
    return nil
}

Stacking defers execute in LIFO order (last deferred, first executed):

Go
fmt.Println("1")
defer fmt.Println("2")
defer fmt.Println("3")
// Output: 1, 3, 2

Closures

Functions that capture variables from their surrounding scope:

Go
func counter() func() int {
    count := 0
    return func() int {
        count++
        return count
    }
}

c := counter()
fmt.Println(c()) // 1
fmt.Println(c()) // 2
fmt.Println(c()) // 3

Tips

  • Check errors at every call site — if err != nil.
  • Use defer for cleanup (closing files, releasing locks).
  • Use closures for stateful functions (counters, generators).
  • Keep functions short — if it does one thing well, it is a good function.

Next: interfaces — Go's secret weapon.