nvanbenschoten / optional

Optional is a library of optional Go types

Home Page:https://godoc.org/github.com/markphelps/optional

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Optional

Build Status Release Software License Go Doc Go Report Card

Optional is a library that provides option types for the primitive Go types.

It can also be used as a tool to generate option type wrappers around your own types.

Motivation

In Go, variables declared without an explicit initial value are given their zero value. Most of the time this is what you want, but sometimes you want to be able to tell if a variable was set or if it's just a zero value. That's where option types come in handy.

This repository is a fork of github.com/markphelps/optional, which also provides option types for Go primitives and custom types. The driving motivation for this fork is performance. Where the original project uses pointer indirection to implement option types, this project inlines values directly into their option wrapper. This inlining avoids memory allocations, reduces garbage collection pressure, and improves cache locality.

Inspiration

Library

The library provides option types each primitive Go type:

Usage

package main

import (
	"fmt"

	"github.com/nvanbenschoten/optional"
)

func main() {
	s := optional.MakeString("foo")

	if s.Present() {
		fmt.Println(s.Get())
	} else {
		fmt.Println("missing")
	}

	if val, ok := s.GetOrBool(); ok {
		fmt.Println(val)
	} else {
		fmt.Println("missing")
	}

	if val, err := s.GetOrErr(); err != nil {
		fmt.Println(err)
	} else {
		fmt.Println(val)
	}

	t := optional.String{}
	fmt.Println(t.GetOr("bar"))
}

See example_test.go and the documentation for more usage.

Memory Layout

Each option type is composed of its value type and a bool inlined into a single struct. For example, the option type for an int32 is represented as struct{int32, bool}. The precise memory layout that this structure translates to is subject to Go's type alignment and padding rules, which make little in the way of guarantees around struct field alignment.

In practice, however, the memory layout for this struct looks like:

+---+---+---+---+---+---+---+---+
| i | i | i | i | b | p | p | p |
+---+---+---+---+---+---+---+---+

i = int32   byte
b = bool    byte
p = padding byte

total size = 8 bytes

The following table lists the memory footprint in bytes of each of the option types provided by the library when compiled for a 64-bit CPU architecture using Go 1.13.9:

Type Size (bytes)
Bool 2
Byte 2
Complex128 24
Complex64 12
Error 24
Float32 8
Float64 16
Int 16
Int16 4
Int32 8
Int64 16
Int8 2
Rune 8
String 24
Uint 16
Uint16 4
Uint32 8
Uint64 16
Uint8 2
Uintptr 16

These sizes will differ depending on target CPU architecture and may change in future compiler versions. Changes here will break the tests in size_test.go.

No effort has been made to specialize the implementation of specific option types in order to optimize for memory size. Future work may explore such optimizations in a manner akin to Rust's "niche-filling strategy".

Tool

The tool can generate option type wrappers around your own types.

Install

go get -u github.com/nvanbenschoten/optional/cmd/optional

Usage

Typically this process would be run using go generate, like this:

//go:generate optional -type=Foo

running this command:

optional -type=Foo

in the same directory will create the file optional_foo.go containing a definition of:

type OptionalFoo struct {
  ...
}

The default type is OptionalT or optionalT (depending on if the type is exported) and output file is optional_t.go. This can be overridden with the -output flag.

Test Coverage

As you can see test coverage is a bit lacking for the library. This is simply because testing generated code is not super easy. I'm currently working on improving test coverage for the generated types, but in the meantime checkout string_test.go and int_test.go for examples.

Also checkout:

Golden Files

If changing the API you may need to update the golden files for your tests to pass by running:

go test ./cmd/optional/... -update.

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Add some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create a new Pull Request

About

Optional is a library of optional Go types

https://godoc.org/github.com/markphelps/optional

License:MIT License


Languages

Language:Go 96.0%Language:Makefile 4.0%