Golang Functional Options pattern

Published on March 14, 2023

Functional options pattern implementation in Golang helps configure and initialize your structs expressive and flexible way.

person := NewPerson(
  WithName("Alice"),
  WithAge(21),
)

Define structure and constructor

First, describe your struct. There we will consider Person struct with three fields.

// Person - describes some person
type Person struct {
  Name    string
  Age     int
  Married bool
}

// PersonOption is option function type for Person type
type PersonOption func(person *Person)

// NewPerson - constructor for Person
func NewPerson(options ...PersonOption) Person {
	// create instance with default values
	person := Person{
		Name:    "Bob",
		Age:     23,
		Married: false,
	}
	for _, optionFunc := range options {
		optionFunc(&person)
	}
	return person
}

Take closer look at the PersonOption type, it gets only pointer for Person instance and returns nothing. Getting structure by pointer not by value is critical option functions. Every function type of PersonOption can mutate given Person structure instance. Second important part is constructor. Instead of getting certain values for person fields, constructor gets an options slice. Constructor creates new instance of Person, iterate over given options and apply each options to person instance.

Define options

// WithName - sets name for Person
func WithName(name string) PersonOption {
	return func(person *Person) {
		person.Name = name
	}
}

The idea is simple - we define function returning option function. More detailed: WithName func gets person name as arguments, and returns new function. This new function has access to name argument as closure, and writes values of argument to Name field of person, when called.

Define additional options below:

// WithAge - sets age for Person
func WithAge(age int) PersonOption {
	return func(person *Person) {
		person.Age = age
	}
}

// Married - sets Person as married
func Married() PersonOption {
	return func(person *Person) {
		person.Married = true
	}
}

// Single - sets Person as single
func Single() PersonOption {
	return func(person *Person) {
		person.Married = false
	}
}

Usage example:

person := NewPerson(
  WithName("Alice"),
  WithAge(21),
  Single()
)

Conclusion

Now you can create instances of struct defining with parameters than you need only. Also you can to create more complex options feet to your needs. You can use one constructor method with different numbers of arguments. Option functions pattern helps you create flexible, expressive and easy to understand API.