Go by Example

Functions

Declaration, multiple return values, named return values, variadic functions, and functions as first-class values.

Functions are the primary unit of code in Go. Go functions can return multiple values, accept a variable number of arguments, and be assigned to variables or passed as arguments - they are first-class values.

A function declaration names the function, lists its parameters with types, and lists its return types. When there is more than one return value, wrap the types in parentheses.

package main
 
import "fmt"
 
// Single return value
func double(n int) int {
    return n * 2
}
 
// Multiple return values - result and error
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, fmt.Errorf("division by zero")
    }
    return a / b, nil
}
 
func main() {
    fmt.Println(double(7)) // 14
 
    result, err := divide(10, 3)
    if err != nil {
        fmt.Println("error:", err)
        return
    }
    fmt.Printf("%.4f\n", result) // 3.3333
}

A variadic function accepts zero or more arguments for its final parameter. Inside the function the variadic parameter behaves as a slice. Pass an existing slice with the ... spread operator.

package main
 
import "fmt"
 
func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}
 
func main() {
    fmt.Println(sum(1, 2, 3))       // 6
    fmt.Println(sum(1, 2, 3, 4, 5)) // 15
 
    nums := []int{10, 20, 30}
    fmt.Println(sum(nums...)) // 60 - spread slice
}

Functions are first-class values in Go - you can assign them to variables, pass them as arguments, and return them from other functions. The blank identifier _ discards an unwanted return value.

package main
 
import (
    "fmt"
    "strconv"
)
 
func apply(f func(int) int, values []int) []int {
    result := make([]int, len(values))
    for i, v := range values {
        result[i] = f(v)
    }
    return result
}
 
func main() {
    triple := func(n int) int { return n * 3 }
 
    nums := []int{1, 2, 3, 4}
    fmt.Println(apply(triple, nums)) // [3 6 9 12]
 
    // _ discards an unwanted return value
    n, _ := strconv.Atoi("42") // discard the error; input is known valid
    fmt.Println(n)             // 42
}

In production

Return (result, error) - do not use output parameters (pointer arguments as return slots). Named return values are acceptable at the top of a function as documentation, but naked returns (a bare return that uses named results implicitly) in long functions destroy readability and make control flow hard to follow during an incident. Prefer explicit returns in all but the shortest helpers. The idempotency key pattern is a case where a clean (string, error) signature avoids the temptation to use an in-out pointer parameter that would obscure whether the key was generated or retrieved.

Enjoyed this? Get more essays on software craft delivered to your inbox.

Subscribe free