> ## Content Index
> Fetch the complete content index at: https://huizhou92.com/llms.txt
> Use this file to discover other available public pages before exploring further.

# Go program pattern 01: Functional Options Pattern
- URL: https://huizhou92.com/go-program-pattern-01-functional-options-pattern-2/
- Published: 2024-01-23T04:00:43.000Z
- Updated: 2024-01-23T04:00:43.000Z
- Description: Go program pattern 01: Functional Options Pattern. Hello everyone, I am hxzhouh. Go is not a fully object-oriented language, and some object-oriented patte。
- Author: huizhou92
- Tags: #Migrated-1788833207488, #Import 2026-09-08 02:07

Hello everyone, I am hxzhouh.

Go is not a fully object-oriented language, and some object-oriented patterns are not well-suited for it. However, over the years, Go has developed its own set of patterns. Today, I would like to introduce a common pattern: the Functional Options Pattern.

> Go programming pattern series articles

[Go program pattern 01: Functional Options PatternHello everyone, I am hxzhouh.![](https://cdn-images-1.medium.com/fit/c/160/160/1*Dk1_RiC7jTa_fJV6bCMxjg.png)](https://levelup.gitconnected.com/go-program-pattern-01-functional-options-pattern-cbcee4979f42?ref=huizhou92.com)

[Go program pattern 02： Implementing Class Inheritance and Method Overriding through CompositionHello everyone, I’m hxzhouh.![](https://cdn-images-1.medium.com/fit/c/160/160/0*8yRd9QAWSRVmcy61)](https://levelup.gitconnected.com/go-program-pattern-02-implementing-class-inheritance-and-method-overriding-through-composition-aa8b03f0f4d5?ref=huizhou92.com)

[Go program pattern 03: Inversion of ControlHello everyone, I’m hxzhouh![](https://cdn-images-1.medium.com/fit/c/160/160/0*XU7a_PbIf-aN-K_S)](https://medium.hxzhouh.com/go-program-pattern-03-inversion-of-control-ee15f0544d14?ref=huizhou92.com)

[Go Program pattern 04: Map-ReduceHello everyone, I’m hxzhouh.![](https://cdn-images-1.medium.com/fit/c/160/160/0*nT7B2VhAKGF6qmNT)](https://levelup.gitconnected.com/go-program-pattern-04-map-reduce-ff3bff69e4e8?ref=huizhou92.com)

[Go program pattern 05: decorationsHow to simplify your code with a higher-order function![](https://cdn-images-1.medium.com/fit/c/160/160/0*PZOIhD2jWDkR8gde)](https://levelup.gitconnected.com/go-program-pattern-05-decorations-d108b493145d?ref=huizhou92.com)

### 01 What is the Functional Options Pattern?

Go does not have constructors like other languages. Instead, it typically uses a `New` function to act as a constructor. However, when a structure has many fields that need to be initialized, there are multiple ways to do so. One preferred way is to use the Functional Options Pattern.

The Functional Options Pattern is a pattern for constructing structs in Go. It involves designing a set of expressive and flexible APIs to help configure and initialize the struct.

The [Go Language Specification](https://github.com/uber-go/guide/blob/master/style.md?ref=huizhou92.com#functional-options) by Uber mentions this pattern:

> *Functional options are a pattern in which you declare an opaque `Option` type that records information in some internal structure. You accept these variable numbers of options and operate on the complete information recorded by the options on the internal structure.*

> *Use this pattern for optional parameters in constructors and other public APIs where you expect these parameters to be extended, especially when there are already three or more parameters on these functions.*

### 02 An Example

To better understand this pattern, let’s walk through an example.

Let’s define a `Server` struct:

```go
package main 
 ​ 
 type Server struct { 
     host string 
     port int 
 } 
 ​ 
 func New(host string, port int) *Server { 
     return &Server{ 
         host, 
         port, 
     } 
 } 
 ​ 
 func (s *Server) Start() error { 
     return nil 
 }
```

How do we use it?

```go
func main() { 
     svr := New("localhost", 1234) 
     if err := svr.Start(); err != nil { 
         log.Fatal(err) 
     } 
 }
```

But what if we want to extend the configuration options for the `Server`? There are generally three approaches:

- Declare a new constructor function for each different configuration option.
- Define a new `Config` struct to store the configuration information.
- Use the Functional Options Pattern.

### Approach 1: Declare a new constructor function for each different configuration option

This approach involves defining dedicated constructor functions for different options. Let’s say we added two fields to the `Server` struct:

```go
type Server struct { 
     host    string 
     port    int 
     timeout time.Duration 
     maxConn int 
 }
```

Typically, `host` and `port` are required fields, while `timeout` and `maxConn` are optional. We can keep the original constructor function and assign default values to these two fields:

```go
func New(host string, port int) *Server { 
     return &Server{ 
         host, 
         port, 
         time.Minute, 
         100, 
     } 
 }
```

Then, we can provide two additional constructor functions for `timeout` and `maxConn`:

```go
func NewWithTimeout(host string, port int, timeout time.Duration) *Server { 
     return &Server{ 
         host, 
         port, 
         timeout, 
         100, 
     } 
 } 
 ​ 
 func NewWithTimeoutAndMaxConn(host string, port int, timeout time.Duration, maxConn int) *Server { 
     return &Server{ 
         host, 
         port, 
         timeout, 
         maxConn, 
     } 
 }
```

This approach works well for configurations that are unlikely to change frequently. Otherwise, you would need to create new constructor functions every time you need to add a new configuration. This approach is used in the Go standard library, such as the `Dial` and `DialTimeout` functions in the `net` package:

```go
func Dial(network, address string) (Conn, error) 
func DialTimeout(network, address string, timeout time.Duration) (Conn, error)
```

### Approach 2: Use a dedicated configuration struct

This approach is also common, especially when there are many configuration options. Typically, you create a `Config` struct that contains all the configuration options for the `Server`. This approach allows for easy extension without breaking the API of the `Server`, even when adding more configuration options in the future.

```go
type Server struct { 
     cfg Config 
 } 
 ​ 
 type Config struct { 
     Host    string 
     Port    int 
     Timeout time.Duration 
     MaxConn int 
 } 
 ​ 
 func New(cfg Config) *Server { 
     return &Server{ 
         cfg, 
     } 
 }
```

When using this approach, you need to construct a `Config` instance first, which brings us back to the original problem of configuring the `Server`. If you modify the fields in `Config`, you may need to define a constructor function for `Config` if the fields are changed to private.

### Approach 3: Use the Functional Options Pattern

A better solution is to use the Functional Options Pattern.

In this pattern, we define an `Option` function type:

```go
type Option func(*Server)
```

The `Option` type is a function type that takes a `*Server` parameter. Then, the constructor function for `Server` accepts a variable number of `Option` types as parameters:

```go
func New(options ...Option) *Server { 
    svr := &Server{} 
    for _, f := range options { 
        f(svr) 
    } 
    return svr 
}
```

How do the options work? We need to define a series of related functions that return `Option`:

```go
func WithHost(host string) Option { 
    return func(s *Server) { 
        s.host = host 
    } 
}
```

```go
func WithPort(port int) Option { 
    return func(s *Server) { 
        s.port = port 
    } 
} 
func WithTimeout(timeout time.Duration) Option { 
    return func(s *Server) { 
        s.timeout = timeout 
    } 
} 
func WithMaxConn(maxConn int) Option { 
    return func(s *Server) { 
        s.maxConn = maxConn 
    } 
}
```

To use this pattern, the client code would look like this:

```go
package main 
import ( 
    "log" 
    "server" 
) 
func main() { 
    svr := New( 
        WithHost("localhost"), 
        WithPort(8080), 
        WithTimeout(time.Minute), 
        WithMaxConn(120), 
    ) 
    if err := svr.Start(); err != nil { 
        log.Fatal(err) 
    } 
}
```

Adding new options in the future only requires adding corresponding `WithXXX` functions.

This pattern is widely used in third-party libraries, such as `github.com/gocolly/colly`:

```go
type Collector struct { 
    // ... 
}
```

```go
func NewCollector(options ...CollectorOption) *Collector 
// Defines a series of CollectorOptions 
type CollectorOption struct { 
    // ... 
} 
func AllowURLRevisit() CollectorOption 
func AllowedDomains(domains ...string) CollectorOption 
...
```

However, when Uber’s Go Programming Style Guide mentions this pattern, it suggests defining an `Option` interface instead of an `Option` function type. This `Option` interface has an unexported method, and the options are recorded in an unexported `options` struct.

Can you understand Uber’s example?

```go
type options struct { 
    cache  bool 
    logger *zap.Logger 
}
```

```go
type Option interface { 
    apply(*options) 
} 
type cacheOption bool 
func (c cacheOption) apply(opts *options) { 
    opts.cache = bool(c) 
} 
func WithCache(c bool) Option { 
    return cacheOption(c) 
} 
type loggerOption struct { 
    Log *zap.Logger 
} 
func (l loggerOption) apply(opts *options) { 
    opts.logger = l.Log 
} 
func WithLogger(log *zap.Logger) Option { 
    return loggerOption{Log: log} 
} 
// Open creates a connection. 
func Open( 
    addr string, 
    opts ...Option, 
) (*Connection, error) { 
    options := options{ 
        cache:  defaultCache, 
        logger: zap.NewNop(), 
    } 
    for _, o := range opts { 
        o.apply(&options) 
    } 
    // ... 
}
```

### 03 Summary

In real-world projects, when dealing with a large number of options or options from different sources (e.g., from files or environment variables), consider using the Functional Options Pattern.

Note that in actual work, we should not rigidly apply the pattern as described above. For example, in Uber’s example, the `Open` function does not only accept a variable number of `Option` parameters because the `addr` parameter is required. Therefore, the Functional Options Pattern is more suitable for cases with many configurations and optional parameters.

References:

- [https://golang.cafe/blog/golang-functional-options-pattern.html](https://golang.cafe/blog/golang-functional-options-pattern.html?ref=huizhou92.com)
- [https://github.com/uber-go/guide/blob/master/style.md#functional-options](https://github.com/uber-go/guide/blob/master/style.md?ref=huizhou92.com#functional-options)

If you found my article enjoyable, feel free to follow me and give it a 👏. Your support would be greatly appreciated.

[文章索引](/article-index/)