option

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Feb 12, 2025 License: Apache-2.0 Imports: 5 Imported by: 2

Documentation

Overview

Package optional provides an Option type for Go. A value of the Option type will be in one of two states: "Some" (containing a value) or "None" (containing no value).

The purpose of the Option type is to more clearly distinguish the two situations that standard Go uses pointer types for:

  • to provide a way to edit the inside of a value without changing the value itself
  • to allow for a value to be either present or absent (with absence being represented as "nil")

Using the Option type, pointers may be reserved for the first purpose. For instance, a type like *int32 would clearly represent an editable value, whereas Option[int32] clearly represents an optional value.

Clean import guarantee

This package is supposed to be used as a dot-import:

import . "github.com/majewsky/go-option"

To avoid backwards incompatibilities, we guarantee that, at least throughout the 1.x series, newer versions of this package will never export any more names than it currently does ("None", "Option" and "Some").

Marshalling considerations

Marshaling into and from YAML using https://github.com/go-yaml/yaml is supported. The "omitempty" flag works as expected.

Marshaling into and from JSON using encoding/json is supported, but the "omitempty" flag does not work. You must use the "omitzero" flag to get the same effect, but note that this flag is only supported by Go 1.24 and newer.

How to replace pointer types with Option types

This abridged example shows the most common ways to interact with pointer types that represent optional values:

type ServerConfiguration struct {
	CrashLogPath *string
	ListenAddress *string
	ThreadCount *uint64
}

func RunServer(cfg ServerConfiguration) {
	// inform about a value being absent
	if cfg.CrashLogPath == nil {
		log.Print("crash logging disabled because no CrashLogPath is given")
	}

	// fill a default value if nil is given
	listenAddress := "127.0.0.1:8080"
	if cfg.ListenAddress != nil {
		listenAddress = *listenAddress
	}

	// access contained value if present, or proceed without contained value if absent
	if cfg.ThreadCount != nil {
		startMultiThreadedServer(listenAddress, *cfg.ThreadCount)
	} else {
		startSingleThreadedServer(listenAddress)
	}
}

This is how the same code snippet looks when replacing the pointer types with Option types and taking advantage of the methods on type Option:

type ServerConfiguration struct {
	CrashLogPath Option[string]
	ListenAddress Option[string]
	ThreadCount Option[uint64]
}

func RunServer(cfg ServerConfiguration) {
	// "if x == nil" becomes "if x.IsNone()"; the opposite check is called IsSome()
	if cfg.CrashLogPath.IsNone() {
		log.Print("crash logging disabled because no CrashLogPath is given")
	}

	// default values can be filled with UnwrapOr()
	listenAddress := cfg.ListenAddress.UnwrapOr("127.0.0.1:8080")

	// Unpack() provides the contained value and a success flag, similar to the double-return-value form of map indexing
	if threadCount, ok := cfg.ThreadCount.Unpack(); ok {
		startMultiThreadedServer(listenAddress, *cfg.ThreadCount)
	} else {
		startSingleThreadedServer(listenAddress)
	}
}

Differences to Rust

This package's API is obviously modeled after the Option type in the Rust standard library, but with some exceptions.

  • Go does not allow to introduce additional type parameters for individual methods. Any methods that, in Rust, introduce the second type parameter U, cannot be represented in Go. Some methods like and() or zip() could be allowed if the argument is restricted to Option[T] instead of Option[U], but this restriction degrades their usefulness beyond reasonable limits.
  • Go does not allow to introduce additional type restrictions in individual methods. This makes methods like unzip() or cloned() unrepresentable in Go. We might make these available as free-standing functions in the future, but if we do, they will definitely not be in this package (see "Clean import guarantee" above).
  • Mixing of struct receiver methods and pointer receiver methods on the same type is discouraged to avoid unintentional copies and data races. Since most of the useful methods require only a struct receiver, we forego those that require a pointer receiver, like get_or_insert() or take(). The only exception to this is the methods implementing Unmarshaler interfaces, where concurrency bugs are very unlikely.

Finally, the unwrap() function is not provided since its meaningless error message is not helpful in most contexts. We only provide expect(), but under the different name UnwrapOrPanic(), since expect() reads terribly in context. Consider the following example:

dbURL := GetDatabaseURLFromEnvironment().Expect("no DB connection found")
// ^ This reads like we *expect* to not find a DB connection, even though the opposite is true.

dbURL := GetDatabaseURLFromEnvironment().UnwrapOrPanic("no DB connection found")
// ^ This is a much clearer phrasing.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Option

type Option[T any] struct {
	// contains filtered or unexported fields
}

Option is a type that contains either one or no instances of T.

func None

func None[T any]() Option[T]

None constructs an Option instance that contains no value.

func Some

func Some[T any](value T) Option[T]

Some constructs an Option instance that contains the provided value.

func (Option[T]) AsPointer

func (o Option[T]) AsPointer() *T

AsPointer converts this Option into a pointer type.

Usage of this function is discouraged because it breaks the clear distinction between interior mutability (pointer) vs. optionality (Option). It is provided for when an Option value needs to be passed to a library function that requires a pointer value.

func (Option[T]) AsSlice

func (o Option[T]) AsSlice() []T

AsSlice returns a slice with either the contained value or nothing in it.

func (Option[T]) Filter

func (o Option[T]) Filter(predicate func(T) bool) Option[T]

Filter removes the contained value (if any) from the Option if it does not match the predicate.

func (Option[T]) Format

func (o Option[T]) Format(f fmt.State, verb rune)

Format implements the fmt.Formatter interface.

If there is a contained value, it will be formatted as if it was given directly. Otherwise, the string "<none>" will be formatted according to the specified width and flags.

func (Option[T]) IsNone

func (o Option[T]) IsNone() bool

IsNone returns whether the Option contains no value. Its inverse is IsSome().

func (Option[T]) IsNoneOr

func (o Option[T]) IsNoneOr(predicate func(T) bool) bool

IsNoneOr returns whether the Option is either empty, or contains a value that matches the given predicate.

If the predicate compares against the zero value, use options.IsNoneOrZero() instead.

func (Option[T]) IsSome

func (o Option[T]) IsSome() bool

IsSome returns whether the Option contains a value. Its inverse is IsNone().

func (Option[T]) IsSomeAnd

func (o Option[T]) IsSomeAnd(predicate func(T) bool) bool

IsSomeAnd returns whether the Option contains a value that matches the given predicate.

func (Option[T]) IsZero

func (o Option[T]) IsZero() bool

IsZero implements the IsZeroer interface as understood by encoding/json and github.com/go-yaml/yaml. It is an alias of IsNone().

func (Option[T]) Iter

func (o Option[T]) Iter() iter.Seq[T]

Iter returns an iterator that yields the contained value once (if any). If the Option is empty, the iterator yields nothing.

func (Option[T]) MarshalJSON

func (o Option[T]) MarshalJSON() ([]byte, error)

MarshalJSON implements the encoding/json.Marshaler interface.

func (Option[T]) MarshalYAML

func (o Option[T]) MarshalYAML() (any, error)

MarshalYAML implements the yaml.Marshaler interface from gopkg.in/yaml.v2 and v3.

func (Option[T]) Or

func (o Option[T]) Or(other Option[T]) Option[T]

Or returns the option itself if it contains a value, or otherwise returns "other".

If you are passing the result of a function call, consider using OrElse() to avoid calling the function unless necessary.

func (Option[T]) OrElse

func (o Option[T]) OrElse(closure func() Option[T]) Option[T]

Or returns the option itself if it contains a value, or otherwise runs the provided closure to produce the return value.

func (*Option[T]) Scan

func (o *Option[T]) Scan(src any) error

Scan implements the database/sql.Scanner interface.

func (*Option[T]) UnmarshalJSON

func (o *Option[T]) UnmarshalJSON(buf []byte) error

UnmarshalJSON implements the encoding/json.Unmarshaler interface.

func (*Option[T]) UnmarshalYAML

func (o *Option[T]) UnmarshalYAML(unmarshal func(any) error) error

UnmarshalYAML implements the yaml.Unmarshaler interface from gopkg.in/yaml.v2.

gopkg.in/yaml.v3 supports this interface via backwards-compatibility, so we intentionally do not use the v3-only signature that refers to the yaml.Node type.

func (Option[T]) Unpack

func (o Option[T]) Unpack() (T, bool)

Unpack returns the contained value (or the zero value if None), as well as if there was a contained value.

func (Option[T]) UnwrapOr

func (o Option[T]) UnwrapOr(fallback T) T

UnwrapOr returns the contained value. If the Option is empty, the provided fallback value is returned instead.

func (Option[T]) UnwrapOrElse

func (o Option[T]) UnwrapOrElse(closure func() T) T

UnwrapOrElse returns the contained value. If the Option is empty, the provided closure is used to produce the return value.

func (Option[T]) UnwrapOrPanic

func (o Option[T]) UnwrapOrPanic(msg any) T

UnwrapOrPanic returns the contained value, or panics with the given error message if it is empty.

func (Option[T]) UnwrapOrPanicf

func (o Option[T]) UnwrapOrPanicf(msg string, args ...any) T

UnwrapOrPanicf is a shorthand for UnwrapOrPanic(fmt.Sprintf(msg, args...)).

func (Option[T]) Value

func (o Option[T]) Value() (driver.Value, error)

Value implements the database/sql/driver.Valuer interface.

func (Option[T]) Xor

func (o Option[T]) Xor(other Option[T]) Option[T]

Xor returns an option containing a value if exactly one of the two given options contains a value, or None otherwise.

Jump to

Keyboard shortcuts

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