About

This post walks through the functional options pattern and why I like it for building Go APIs that stay tidy as they grow.

Background

Overview

I first ran into this pattern while using the goldmark library (a great Go markdown library). At the time it felt a little mysterious, so I filed it away and moved on. Later, when I started writing my own libraries, I wanted the APIs to feel idiomatic, so I circled back and learned how the functional options pattern works.

Pattern

The functional options pattern is a simple way to configure a struct without stuffing a constructor with lots of parameters.

Instead of passing a long list of values, you pass option functions to New. Each option function receives a pointer to the struct and sets one field.

Option functions are usually small helpers like SetName or SetAge. They capture a value, return a function, and New runs them to apply those values.

Experiment

Overview

The example below defines a User struct and a few package functions that configure it:

package user

type User struct {
  name string
  age  uint8
}

type Option func(*User)

func New(opts ...Option) *User {
  u := &User{name: "unknown", age: uint8(0)}
  for _, set := range opts {
    set(u)
  }
  return u
}

func SetName(name string) Option {
  return func(u *User) {
    u.name = name
  }
}

func SetAge(age uint8) Option {
  return func (u *User) {
    u.age = age
  }
}
package main

import (
  "user"
)

func main() {
  u := user.New(
    user.SetName("Mario"),
    user.SetAge(uint8(37))
  )
}

Explanation