go 50 lines · 7 steps

The functional options pattern in Go

Configure a struct with any subset of settings by passing composable functions instead of a giant argument list.

Explained by highlit
1package config
2 
3import "time"
4 
5type ServerConfig struct {
6 Host string
7 Port int
8 ReadTimeout time.Duration
9 WriteTimeout time.Duration
10 MaxConnections int
11 TLSEnabled bool
12 AllowedOrigins []string
13}
14 
15type Option func(*ServerConfig)
16 
17func WithHost(host string) Option {
18 return func(c *ServerConfig) { c.Host = host }
19}
20 
21func WithPort(port int) Option {
22 return func(c *ServerConfig) { c.Port = port }
23}
24 
25func WithReadTimeout(d time.Duration) Option {
26 return func(c *ServerConfig) { c.ReadTimeout = d }
27}
28 
29func WithMaxConnections(n int) Option {
30 return func(c *ServerConfig) { c.MaxConnections = n }
31}
32 
33func WithTLS(enabled bool) Option {
34 return func(c *ServerConfig) { c.TLSEnabled = enabled }
35}
36 
37func WithAllowedOrigins(origins ...string) Option {
38 return func(c *ServerConfig) {
39 c.AllowedOrigins = append([]string(nil), origins...)
40 }
41}
42 
43func (base ServerConfig) With(opts ...Option) ServerConfig {
44 clone := base
45 clone.AllowedOrigins = append([]string(nil), base.AllowedOrigins...)
46 for _, opt := range opts {
47 opt(&clone)
48 }
49 return clone
50}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Functional options let callers set only the fields they care about while everything else keeps its zero or default value.
  2. 2Returning closures over a shared config pointer keeps each option small, self-contained, and independently testable.
  3. 3Copying slices during a clone prevents callers from mutating each other's shared backing arrays.

Related explainers

Share this explainer

Here's the card — post it anywhere.

The functional options pattern in Go — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code