Learn › Go Programming

Functions & Methods

Master Go functions including multiple return values, variadic parameters, closures, and methods on types.

Multiple Return Values

One of Go's distinctive features is the ability for functions to return multiple values. This is most commonly used to return a result along with an error value, establishing Go's idiomatic error handling pattern. Named return values allow you to give names to the return parameters, which serve as documentation and allow the use of a bare 'return' statement. Multiple return values eliminate the need for out parameters or special wrapper types that other languages require.

package main

import (
    "errors"
    "fmt"
)

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

// Named return values
func swap(x, y string) (first, second string) {
    first = y
    second = x
    return
}

func main() {
    result, err := divide(10, 3)
    if err != nil {
        fmt.Println("Error:", err)
        return
    }
    fmt.Printf("10 / 3 = %.2f\n", result)

    a, b := swap("hello", "world")
    fmt.Println(a, b)
}

Variadic Functions

Variadic functions accept a variable number of arguments of the same type, specified using the '...' syntax before the type name. The variadic parameter is received as a slice inside the function, allowing you to iterate over it normally. Only the last parameter of a function can be variadic. You can pass an existing slice to a variadic function by appending '...' to the slice argument.

package main

import "fmt"

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

func printAll(sep string, values ...string) {
    for i, v := range values {
        if i > 0 {
            fmt.Print(sep)
        }
        fmt.Print(v)
    }
    fmt.Println()
}

func main() {
    fmt.Println(sum(1, 2, 3))         // 6
    fmt.Println(sum(10, 20, 30, 40))  // 100

    numbers := []int{5, 10, 15}
    fmt.Println(sum(numbers...))       // 30

    printAll(", ", "Go", "is", "great")
}

Closures

Go supports anonymous functions and closures, which are functions that capture and reference variables from their enclosing scope. Closures are particularly useful for creating function factories, implementing callbacks, and maintaining state without using global variables. Each closure has its own copy of the captured variables, and modifications to those variables persist between calls to the closure. This pattern is widely used in Go for middleware, event handlers, and iterators.

package main

import "fmt"

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

func multiplier(factor int) func(int) int {
    return func(x int) int {
        return x * factor
    }
}

func main() {
    next := counter()
    fmt.Println(next()) // 1
    fmt.Println(next()) // 2
    fmt.Println(next()) // 3

    double := multiplier(2)
    triple := multiplier(3)
    fmt.Println(double(5))  // 10
    fmt.Println(triple(5))  // 15
}

Methods on Types

Methods in Go are functions with a special receiver argument that associates them with a particular type. The receiver appears between the 'func' keyword and the method name, and can be either a value receiver or a pointer receiver. Value receivers work on a copy of the value, while pointer receivers can modify the original value and avoid copying large structs. Methods can be defined on any named type in the same package, not just structs.

package main

import (
    "fmt"
    "math"
)

type Circle struct {
    Radius float64
}

// Value receiver
func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

// Pointer receiver - can modify the struct
func (c *Circle) Scale(factor float64) {
    c.Radius *= factor
}

type Celsius float64

func (c Celsius) ToFahrenheit() float64 {
    return float64(c)*9/5 + 32
}

func main() {
    c := Circle{Radius: 5}
    fmt.Printf("Area: %.2f\n", c.Area())

    c.Scale(2)
    fmt.Printf("Scaled area: %.2f\n", c.Area())

    temp := Celsius(100)
    fmt.Printf("%.0f°C = %.0f°F\n", float64(temp), temp.ToFahrenheit())
}

← Variables & Types · Control Flow →