Skip to content

Chapter 3 of 7

Functions, Methods, and Interfaces

Functions in Go are first-class values: they can be assigned to variables, passed as arguments, returned from other functions, and stored in maps. A variadic parameter func sum(nums ...int) appears inside the function as a []int, and callers can either spread individual arguments or pass a slice with s.... Higher-order functions such as sort.Slice, http.HandlerFunc, and many generic helpers accept function arguments, while anonymous function literals (add := func(a, b int) int { return a + b }) are commonly passed inline to go, defer, and callback APIs. A closure is a function literal that captures variables from its enclosing scope, which is why the classic counter factory returns a function that retains and updates n across calls.

Methods extend a named type by attaching a receiver. The choice between a value receiver (t T) and a pointer receiver (t *T) has concrete consequences: a value receiver operates on a copy and cannot mutate the original, while a pointer receiver modifies the actual value and avoids copying large structs. Because of how method sets are defined, the method set of *T includes both pointer-receiver and value-receiver methods, but the method set of T only includes value-receiver methods. This asymmetry affects interface satisfaction and is the reason receivers should be consistent within a type. A method value such as t.Add binds the method to a specific receiver, producing a closure-like value that can be passed around, while a method expression like (*Time).Add produces a function whose first parameter is the receiver.

Interfaces are Go's primary abstraction mechanism, and unlike Java or C# they are satisfied implicitly. A type satisfies an interface simply by implementing all of its methods; no implements keyword is required. This structural approach encourages small, focused interfaces such as io.Reader and fmt.Stringer. The empty interface interface{} (aliased as any since Go 1.18) is satisfied by every type, making it the universal container, but extracting the concrete value requires a type assertion v, ok := i.(string) or a type switch switch v := i.(type). Compile-time interface satisfaction can be enforced with a blank assignment such as var _ io.Reader = (*MyType)(nil). A subtle pitfall involves typed-nil interfaces: assigning a nil pointer to an interface variable does not produce a nil interface, so callers should check the underlying pointer explicitly. Finally, Go 1.18 added generics, allowing type-parameterized functions and types like func Map[T any, U any](s []T, f func(T) U) []U, which dramatically reduces the need for code generation or interface{}-based containers.

All chapters
  1. 1Language Fundamentals and Project Structure
  2. 2Composite Types and Data Structures
  3. 3Functions, Methods, and Interfaces
  4. 4Error Handling, defer, and Formatting
  5. 5Concurrency
  6. 6Standard Library: I/O, Encoding, Web, and Time
  7. 7Testing, Tooling, and Runtime Internals

Drill it

Reading is not remembering. These come from the Go Programming deck:

Q

What is Go (Golang)?

Go is an open-source programming language developed at Google by Robert Griesemer, Rob Pike, and Ken Thompson. It is statically typed, compiled, and designed fo...

Q

How do you declare a variable in Go?

You can declare variables using:var x int = 10var y = "hello" (type inferred)z := 42 (short declaration, only inside functions)

Q

What is a goroutine?

A goroutine is a lightweight thread managed by the Go runtime. You start one with the go keyword:go myFunction()Goroutines are multiplexed onto OS threads and a...

Q

How do you create a channel in Go?

Use the make function:ch := make(chan int) — unbufferedch := make(chan string, 5) — buffered with capacity 5Channels are used to communicate between goroutines...