Go: How to Set Default Values in Structs with Maximum Performance

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Go: How to Set Default Values in Structs with Maximum Performance

\n\n

Hello, Go developer! If you've ever wondered: "Why can't I just write default: inside a struct in Go, like in Python or C++?", you've come to the right place. This topic is one of the hottest questions on Stack Overflow (tens of thousands of views!), and today we'll break it down once and for all.

\n\n

Go has no built-in mechanism for default values in Go structs. The language creators deliberately chose this to avoid the magic and complexity inherent in other languages. Instead, they offer elegant and performant patterns. We'll cover them all: from simple constructors to advanced techniques with options. And, of course, we'll measure performance so you can choose code that's not only clean but also fast.

\n\n

Why Doesn't Go Support Default Values "Out of the Box"?

\n\n

Before diving into the code, let's understand the philosophy. In Go, every variable has a zero value. For int it's 0, for string it's "", for pointers and slices it's nil. This is done for predictability and safety. If Go allowed overriding these values in the struct declaration, it would lead to confusion: which value is the "real" one — the one set in the struct, or the one we passed during initialization?

\n\n

Therefore, the Go community has developed several idiomatic ways to solve the problem of setting default values in Go structs. Let's break them down from simple to complex.

\n\n

Method 1: Constructor Function (New()) — The Gold Standard

\n\n

This is the most popular and recommended approach. Instead of creating a struct directly, we write a function that returns an instance with pre-set fields.

\n\n
package config\n\ntype AppConfig struct {\n    Port    int\n    Timeout time.Duration\n    Debug   bool\n    DBHost  string\n}\n\n// NewAppConfig — constructor with default values\nfunc NewAppConfig() AppConfig {\n    return AppConfig{\n        Port:    8080,\n        Timeout: 30 * time.Second,\n        Debug:   false,\n        DBHost:  "localhost",\n    }\n}
\n\n

Usage:

\n
cfg := config.NewAppConfig()\nfmt.Println(cfg.Port) // 8080
\n\n

Why is this great? It's explicit, readable, and performant. The Go compiler easily optimizes such code. No reflection, no extra allocations — just returning a struct by value.

\n\n

Benchmark (simplified):

\n
func BenchmarkNewAppConfig(b *testing.B) {\n    for i := 0; i < b.N; i++ {\n        _ = NewAppConfig()\n    }\n}\n// Result: ~0.3 ns/op — practically free
\n\n

Method 2: Functional Options Pattern

\n\n

This pattern has become a hallmark of Go. It's ideal when a struct has many fields, most of which are optional. Instead of passing 10 arguments to a constructor, we use option functions.

\n\n
type Server struct {\n    host    string\n    port    int\n    timeout time.Duration\n    maxConn int\n}\n\ntype Option func(*Server)\n\nfunc WithHost(host string) Option {\n    return func(s *Server) {\n        s.host = host\n    }\n}\n\nfunc WithPort(port int) Option {\n    return func(s *Server) {\n        s.port = port\n    }\n}\n\nfunc WithTimeout(t time.Duration) Option {\n    return func(s *Server) {\n        s.timeout = t\n    }\n}\n\n// NewServer — constructor with defaults\nfunc NewServer(opts ...Option) *Server {\n    // Set default values in Go structs\n    s := &Server{\n        host:    "0.0.0.0",\n        port:    8080,\n        timeout: 30 * time.Second,\n        maxConn: 100,\n    }\n    // Apply the passed options\n    for _, opt := range opts {\n        opt(s)\n    }\n    return s\n}
\n\n

Blogs

Book Recommendations