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
‹ swipe to step through ›
Walkthrough
Space play
←→ step
click any line
Three takeaways
- 1Functional options let callers set only the fields they care about while everything else keeps its zero or default value.
- 2Returning closures over a shared config pointer keeps each option small, self-contained, and independently testable.
- 3Copying slices during a clone prevents callers from mutating each other's shared backing arrays.
Related explainers
go
package streaming import ( "bufio"
Streaming NDJSON logs over HTTP in Go
http-streaming
channels
select
Advanced
10 steps
javascript
function evaluate(expression) { const tokens = tokenize(expression); let pos = 0;
Building a recursive descent calculator
parsing
recursion
operator-precedence
Intermediate
8 steps
go
package api import ( "crypto/sha256"
ETag conditional requests in Gin
http-caching
etag
conditional-requests
Intermediate
6 steps
go
func (w *Watcher) resetDebounce(d time.Duration) { if !w.timer.Stop() { select { case <-w.timer.C:
Debouncing a stream of events in Go
debounce
timers
channels
Advanced
7 steps
go
package logging import ( "context"
Deduplicating log attributes in Go's slog
decorator-pattern
structured-logging
immutability
Intermediate
8 steps
go
package middleware import ( "net/http"
Per-plan export limits in Gin middleware
middleware
rate-limiting
authorization
Intermediate
7 steps
Share this explainer
Here's the card — post it anywhere.
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code
Embed this explainer
Drop the interactive walkthrough into a blog or docs. Views never cost a credit.
<iframe src="https://highlit.co/explainers/the-functional-options-pattern-in-go-explained-go-be23/embed?autoplay=1" width="100%" height="520" loading="lazy" style="border:0"></iframe>
Autoplay is on by default — add ?autoplay=0 to start paused.