Help us shape the future of Go! Take the 2026 Go Developer Survey

defaults

package module
v1.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Sep 13, 2026 License: MIT Imports: 9 Imported by: 986

README

defaults

CI codecov Go Reference GitHub release License

Initialize structs with default values

type Server struct {
	Host    string            `default:"localhost"`
	Port    int               `default:"8080"`
	Timeout time.Duration     `default:"30s"`
	Tags    []string          `default:"[\"web\"]"`
	Labels  map[string]string `default:"{\"env\": \"dev\"}"`
}

var server Server
if err := defaults.Set(&server); err != nil {
	panic(err)
}
// localhost:8080 30s [web] map[env:dev]

Install

$ go get github.com/creasty/defaults

Features

  • Supports almost all kind of types
    • Scalar types
      • int/8/16/32/64, uint/8/16/32/64, float32/64
      • uintptr, bool, string
    • Complex types
      • map, slice, struct
    • Nested types
      • map[K1]map[K2]Struct, []map[K1][]Struct
    • Aliased types
      • time.Duration
      • e.g., type Enum string
    • Pointer types
      • e.g., *SampleStruct, *int
  • Recursively initializes fields in a struct
  • Dynamically sets default values by:
    • Implementing the defaults.Setter interface, or
    • Implementing encoding.TextUnmarshaler, which takes precedence over defaults.Setter -- the tag is handed to UnmarshalText and SetDefaults is not called
  • Preserves non-initial values from being reset with a default value

The API is three functions: Set, MustSet (the same, but panicking), and CanUpdate. Runnable examples for each are on pkg.go.dev, and the package example walks the composite cases in one program.

Behavior

Zero values

A field is written only while it still holds its type's zero value. No scalar type can tell an unspecified value from its zero value — an int left alone is 0, a string is "", a bool is false — so a zero the caller set on purpose is indistinguishable from one never set, and the default replaces it.

Use a pointer where that distinction matters. nil means unspecified, and a pointer to the zero value is preserved: *bool is the way to let false survive a default:"true".

type Feature struct {
	Enabled bool  `default:"true"` // a caller's false is replaced
	Verbose *bool `default:"true"` // a caller's &false survives
}

default:"-" opts a field out entirely: it is neither parsed nor recursed into. That is how a field gets its value from a SetDefaults method instead of a tag.

Durations

A time.Duration takes anything time.ParseDuration accepts. Every field that is an int64 underneath is parsed the same way, though, so a time.Duration takes a bare integer and a plain int64 takes a duration string:

type Job struct {
	Timeout time.Duration `default:"1"`  // 1ns, not 1s
	Retries int64         `default:"5m"` // 300000000000, not an error
}

This is deliberate: a type defined from time.Duration keeps no trace of it at runtime, so parsing durations only for time.Duration itself would stop such types from taking "10s".

Design principles

Keep it simple. The scope is narrow by design — read a tag, fill a field — and is meant to stay that way. Adjacent concerns belong in adjacent libraries.

Stay comprehensive. Broad type support and high test coverage are what make the library trustworthy. Statement coverage is 100% and CI enforces it, and every supported type has a test pinning its behavior — including the behavior that looks wrong, which is pinned with a comment saying so rather than left to be rediscovered.

License

MIT

Documentation

Overview

Example

This example fills a tree of structs, pointers, slices and maps in one call. Every Endpoint the call reaches gets the same treatment wherever it sits: its tags, then its SetDefaults.

package main

import (
	"fmt"
	"net"
	"strconv"

	"github.com/creasty/defaults"
)

// Endpoint implements defaults.Setter for Example below.
type Endpoint struct {
	Host string `default:"localhost"`
	Port int    `default:"8080"`
	Addr string `default:"-"`
}

// SetDefaults derives Addr from Host and Port, which the tags have filled by the time it is called.
func (e *Endpoint) SetDefaults() {
	if defaults.CanUpdate(e.Addr) {
		e.Addr = net.JoinHostPort(e.Host, strconv.Itoa(e.Port))
	}
}

func main() {
	type Gateway struct {
		// A struct is filled without needing a tag.
		Listen Endpoint
		// A nil pointer needs a tag to be allocated. The tag's JSON goes in first, and the
		// struct's own tags fill what it leaves out.
		Metrics *Endpoint `default:"{\"Port\": 9090}"`
		// Each element a tag creates is filled too.
		Backends []Endpoint `default:"[{\"Host\": \"app-1\"}, {\"Host\": \"app-2\"}]"`
		// So is each element the caller supplied, keeping the values it already holds.
		Routes map[string]Endpoint
		// An opted-out struct is not descended into, so its SetDefaults is not called either.
		Tracing Endpoint `default:"-"`
	}

	gateway := Gateway{
		Routes: map[string]Endpoint{"/api": {Port: 3000}},
	}
	defaults.MustSet(&gateway)

	fmt.Println(gateway.Listen.Addr)
	fmt.Println(gateway.Metrics.Addr)
	fmt.Println(gateway.Backends[0].Addr, gateway.Backends[1].Addr)
	fmt.Println(gateway.Routes["/api"].Addr)
	fmt.Println(gateway.Tracing == Endpoint{})
}
Output:
localhost:8080
localhost:9090
app-1:8080 app-2:8080
localhost:3000
true

Index

Examples

Constants

This section is empty.

Variables

View Source
var ErrInvalidType = errors.New("not a struct pointer")

ErrInvalidType is the error Set returns, and MustSet panics with, when the argument is not a non-nil pointer to a struct. Test for it with errors.Is.

Functions

func CanUpdate

func CanUpdate(v interface{}) bool

CanUpdate returns true when the given value is an initial value of its type

Example
package main

import (
	"fmt"

	"github.com/creasty/defaults"
)

func main() {
	fmt.Println(defaults.CanUpdate(0), defaults.CanUpdate(123))
	fmt.Println(defaults.CanUpdate(""), defaults.CanUpdate("hello"))
}
Output:
true false
true false

func MustSet added in v1.5.1

func MustSet(ptr interface{})

MustSet function is a wrapper of Set function It will call Set and panic if err not equals nil.

func Set

func Set(ptr interface{}) error

Set initializes members in a struct referenced by a pointer. Maps and slices are initialized by `make` and other primitive types are set with default values. `ptr` should be a non-nil struct pointer, or Set returns ErrInvalidType.

Example
package main

import (
	"fmt"
	"time"

	"github.com/creasty/defaults"
)

func main() {
	type Server struct {
		Host    string            `default:"localhost"`
		Port    int               `default:"8080"`
		Timeout time.Duration     `default:"30s"`
		Tags    []string          `default:"[\"web\"]"`
		Labels  map[string]string `default:"{\"env\": \"dev\"}"`
	}

	var server Server
	if err := defaults.Set(&server); err != nil {
		panic(err)
	}

	fmt.Printf("%s:%d\n", server.Host, server.Port)
	fmt.Println(server.Timeout)
	fmt.Println(server.Tags, server.Labels)
}
Output:
localhost:8080
30s
[web] map[env:dev]
Example (Nested)
package main

import (
	"fmt"

	"github.com/creasty/defaults"
)

func main() {
	type Retry struct {
		Attempts int `default:"3"`
	}
	type Client struct {
		// A struct is descended into with or without a tag, and a pointer needs one to be
		// allocated at all.
		Retry    Retry
		Fallback *Retry `default:"{}"`
		Disabled *Retry
	}

	var client Client
	if err := defaults.Set(&client); err != nil {
		panic(err)
	}

	fmt.Println(client.Retry.Attempts)
	fmt.Println(client.Fallback.Attempts)
	fmt.Println(client.Disabled == nil)
}
Output:
3
3
true

Types

type Setter

type Setter interface {
	SetDefaults()
}

Setter is an interface for setting default values.

Set calls SetDefaults on a struct once it has filled the struct's fields. A SetDefaults the struct has only by promotion from an embedded field is not called on the struct: it belongs to the field, which gets whatever call it would get as a named field.

Example
package main

import (
	"fmt"
	"time"

	"github.com/creasty/defaults"
)

// Config implements defaults.Setter for ExampleSetter below.
type Config struct {
	Retries int           `default:"3"`
	Backoff time.Duration `default:"-"`
}

// SetDefaults derives a default that a tag cannot express, and CanUpdate keeps it from overriding
// what the caller already provided.
func (c *Config) SetDefaults() {
	if defaults.CanUpdate(c.Backoff) {
		c.Backoff = time.Duration(c.Retries) * time.Second
	}
}

func main() {
	var config Config
	if err := defaults.Set(&config); err != nil {
		panic(err)
	}
	fmt.Println(config.Retries, config.Backoff)

	// A value the caller supplied is left alone.
	custom := Config{Backoff: time.Minute}
	if err := defaults.Set(&custom); err != nil {
		panic(err)
	}
	fmt.Println(custom.Retries, custom.Backoff)
}
Output:
3 3s
3 1m0s

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL