Go Cheatsheet

Functions

Use this Go reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.

Declaration Syntax

func name(param1 type1, param2 type2) returnType {
    // body
}
func add(a, b int) int {          // same-type params can share type
    return a + b
}

func greet(name string) string {
    return "Hello, " + name
}

Multiple Return Values

Go functions can return any number of values. Use multiple returns instead of out-parameters.

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, 2)
if err != nil {
    log.Fatal(err)
}

Named Return Values

Named returns double as documentation and enable bare return.

func minMax(nums []int) (min, max int) {
    min, max = nums[0], nums[0]
    for _, v := range nums[1:] {
        if v < min { min = v }
        if v > max { max = v }
    }
    return  // bare return; returns min and max
}

Use named returns sparingly — they can reduce clarity in long functions.

Variadic Functions

...T accepts zero or more arguments of type T. Inside the function it behaves as []T.

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

sum(1, 2, 3)
sum()

// spread a slice
s := []int{1, 2, 3}
sum(s...)

First-Class Functions

Functions are values — pass, return, and store them.

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

// pass as argument
func apply(op func(int, int) int, a, b int) int {
    return op(a, b)
}
apply(add, 2, 3)

// return from function
func multiplier(factor int) func(int) int {
    return func(n int) int {
        return n * factor
    }
}
double := multiplier(2)
double(5)  // 10

Closures

A closure captures variables from its enclosing scope.

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

c := counter()
c()  // 1
c()  // 2
c()  // 3

// Each call to counter() creates its own independent count.
// Common gotcha: loop variable capture
funcs := make([]func(), 5)
for i := 0; i < 5; i++ {
    i := i  // shadow to capture each value
    funcs[i] = func() { fmt.Println(i) }
}

Anonymous Functions (immediately invoked)

result := func(x int) int {
    return x * x
}(5)  // 25

Recursive Functions

func factorial(n int) int {
    if n <= 1 {
        return 1
    }
    return n * factorial(n-1)
}

// Mutual recursion — both must be declared before first call
func isEven(n int) bool { return n == 0 || isOdd(n-1) }
func isOdd(n int)  bool { return n != 0 && isEven(n-1) }

Methods

A method is a function with a receiver. The receiver can be a value or pointer.

type Circle struct {
    Radius float64
}

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

// Pointer receiver — can mutate the original
func (c *Circle) Scale(factor float64) {
    c.Radius *= factor
}

circ := Circle{Radius: 5}
circ.Area()          // 78.54...
circ.Scale(2)        // Radius becomes 10

Rule of thumb: use pointer receivers when the method mutates state, or when the type is large. Be consistent within a type — mix is allowed but confusing.

Pointer vs Value Receiver Summary

ScenarioUse
Must modify receiverPointer *T
Large struct (avoid copy)Pointer *T
Implements an interface requiring a pointerPointer *T
Small, immutable value typeValue T

Function Types as Fields

type Handler func(http.ResponseWriter, *http.Request)

type Middleware struct {
    Before func()
    After  func()
    Handle func(string) string
}

init Functions

init runs automatically after all variable initializations, before main. A package can have multiple init functions (even in the same file).

package main

var db *sql.DB

func init() {
    var err error
    db, err = sql.Open("postgres", os.Getenv("DSN"))
    if err != nil {
        log.Fatal(err)
    }
}

func main() { /* db is ready */ }

Defer Inside Functions

Deferred calls fire on return (including panic). See Control Flow for full defer details.

func writeFile(path, content string) error {
    f, err := os.Create(path)
    if err != nil {
        return err
    }
    defer f.Close()
    _, err = f.WriteString(content)
    return err
}

Function Signature Patterns

// Error-returning constructor
func NewServer(addr string) (*Server, error) { ... }

// Options function pattern
type Option func(*Server)
func WithTimeout(d time.Duration) Option {
    return func(s *Server) { s.timeout = d }
}

// Context-first (idiomatic for cancellable operations)
func Fetch(ctx context.Context, url string) ([]byte, error) { ... }

// Functional options
func NewServer(addr string, opts ...Option) *Server {
    s := &Server{addr: addr}
    for _, o := range opts {
        o(s)
    }
    return s
}

Common Standard Library Functions

// math
math.Abs(x)
math.Sqrt(x)
math.Pow(base, exp)
math.Floor(x), math.Ceil(x), math.Round(x)
math.Min(a, b), math.Max(a, b)
math.Log(x), math.Log2(x), math.Log10(x)
math.Inf(1), math.IsNaN(x), math.IsInf(x, 0)
math.MaxInt, math.MinInt
math.MaxFloat64

// math/rand (v2 in Go 1.22+)
rand.IntN(100)        // [0, 100)
rand.Float64()        // [0.0, 1.0)
rand.Perm(n)          // random permutation

// sort
sort.Ints(s)
sort.Strings(s)
sort.Float64s(s)
sort.Slice(s, func(i, j int) bool { return s[i] < s[j] })
sort.SliceStable(...)
sort.Search(n, f)     // binary search
sort.IntsAreSorted(s)