Danylo's website

Options bag

Word count: ~343

Zig often uses “Options bag” pattern (is it official name?). Lately, I’ve been using it in Go.

Go options bag

package fediverse

type NewClientArgs struct{
	HTTPClient *http.Client
	UseV2      *bool
	FluentOpts []fluent.Opts
}

func (nca NewClientArgs) WithDefaults() NewClientArgs {
	if nca.HTTPClient == nil {
		nca.HTTPClient = http.DefaultClient
	}
	if nca.UseV2 == nil{
		nca.UseV2 = new(viper.GetBool("some_env_var"))
	}
	if *nca.UseV2{
		nca.FluentOpts = append(nca.FluentOpts, fluent.WithSomethingCool, fluent.WithNewGen)
	}
	return nca
}

type Client struct{}

func NewClient(url *url.URL, args NewClientArgs) *Client {
	args = args.WithDefaults()
	return &Client{
		// set fields
		// client: args.HTTPClient, // etc.
	}
}

I like this more: all options are in one place; they can be easily logged; order of initialization is defined and not random.

I think, NewClientOpts name may be more suitable for the case where all fields are allowed to be in zero/default state. But in cases where you need to have required fields in the struct name it ~Args.

Mutating options

In my opinion, Go ecosystem has more than enough argument passing pattern where you pass a slice of interfaces or functions that are going to mutate the state.

type WithOpt func(*privateStruct)

func WithInsanity(level int) WithOpt
func WithPrice(price uint) WithOpt

type privateStruct struct{
	// state
}

func New(opts ...WithOpt) *privateStruct {
	v := &privateStruct{}
	for _, opt := range opts {
		opt(v)
	}
	return v
}

I do not like this approach. We’ve lost: ordering guarantee; easy logging of options; potentially With~ functions may be in different files.

Validation

In case if you need required fields, add Validate method.

type Args struct{
	IsWater      *bool
	IsLiquid     *bool
	CanCatchFire *bool
}

func (a Args) Validate() error {
	var errs []error

	if a.IsWater != nil && a.CanCatchFire != nil{
		errs = append(errs, errors.New("water is not compatible with fire"))
	}

	return errors.Join(errs...)
}
Zig patch VTable