liveBook · Manning

Functional options pattern

As explained in 100 Go Mistakes and How to Avoid Them

The functional options pattern configures an object by passing option functions, commonly through variadic arguments, to a constructor or setup function.

Avoid extending a constructor with new positional parameters when existing callers already depend on its signature. Adding timeout or context arguments to NewServer breaks compatibility; functional options add configuration without changing the mandatory address argument. section 2.11

Keep configuration in an unexported options struct and define Option as a function that receives a pointer to that struct and returns an error. Each With-prefixed function returns a closure that validates its captured value and updates the configuration. section 2.11.3

Applying options while constructing a server

type Option func(options *options) error func NewServer(addr string, opts ...Option) (*http.Server, error) { var options options for _, opt := range opts { if err := opt(&options); err != nil { return nil, err } } // Apply the completed configuration. } server, err := httplib.NewServer("localhost", httplib.WithPort(8080), httplib.WithTimeout(time.Second)) server, err := httplib.NewServer("localhost") The first call supplies multiple options; the second receives the default configuration without an empty options value. section 2.11.3

Apply each option where its behavior belongs:

  • Validate inputs inside the option; WithPort rejects negative values and stores valid values in the configuration. section 2.11.3
  • Process the completed configuration afterward; an unset port uses the default, zero selects a random port, and another value is used directly. section 2.11 section 2.11.3
type options struct {                          #1
    port *int
}
 
type Option func(options *options) error       #2
 
func WithPort(port int) Option {               #3
    return func(options *options) error {
        if port < 0 {
            return errors.New("port should be positive")
        }
        options.port = &port
        return nil
    }
}

Inspect the compact Option and WithPort definitions to see how closures capture values, validate them, and mutate private configuration. section 2.11.3

How 100 Go Mistakes and How to Avoid Them covers it

How deep it goes
Explains private configuration, closure-based options, variadic application, validation, and final port handling.
Examples
Uses an HTTP server with address, port, timeout, default-port, random-port, and negative-port cases.
What it assumes
Assumes familiarity with Go functions and pointers while defining closures and option-function behavior explicitly.
What you can do after
Build API constructors that accept optional, validated configuration without forcing default arguments or breaking existing calls.
How it connects
Connects closures, variadic functions, API compatibility, error handling, and the builder pattern.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of 100 Go Mistakes and How to Avoid Them. The section links point to the parts of the book it draws on.