option

package module
v0.0.0-...-863facc Latest Latest
Warning

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

Go to latest
Published: Oct 7, 2025 License: BSD-2-Clause Imports: 4 Imported by: 90

README

option

Go Reference

Package option provides a missing type to allow modelling optional facts in cases where the natural zero value of a type is not sufficient to express this fact. The need for this is not a problem caused by the expectation, that other language just provides this, but because

  • using pointers to express optionality, has not the same semantics as pointers - and is often not even acceptable when "explicitly" modelling memory layouts.
  • There is no sense in using a pointer to a pointer type and omitting the option fact
  • Pointers introduce more questions around the overloaded meaning of the zero value of a pointer type.
  • Pointers are also used to express a kind of ownership concerns, so that makes the water even more muddy.
  • Semantics with iterators are entirely broken, because there is no iter.Seq3 (T,bool,error) you would need to resort to (*T,error) where *T is for optional cases *T which is against the semantic guidelines, that the tupel of (*T,error) means that if error is nil, *T is not.

To shorten up that discussion, even the standard library provides a generic helper for this in form of the sql.Null type. However, the usage of this type cannot be generally recommended, because:

  • exposing the Valid field and the wrapped T in public fields violates the concept of information hiding and intended consistency of the author, which returns the Option. We experimented with a copy of that and have seen a regular misconception, especially for new programmers, which started to modify the value and the valid flag causing all sorts of confusion, which the usage of the type should have solved, actually. Nonetheless, code using an option type makes the fact a lot better readable and when mixing pointer types if they don't make sense, especially when mixing with ownership concerns.
  • the sql package indicates a persistence concern. It is against our architecture guidelines (and most others) to import persistent dependencies into a domain core.
  • there is no transparent JSON Marshal/Unmarshal support using the natural absent or null representation
  • there is no omitzero support

Why not using other libaries like samber/mo?

  • some do not express it in a clear way
  • have missing json support
  • introduce zero values on accessors, which is the entirely wrong thing. We would not need the Option at all.
  • provide no lazy functors
  • unneeded dependencies

Documentation

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func Must

func Must[T any](v T, err error) T

Must asserts that the tupel of (T,error) does not contain an error and otherwise panics.

Example

Output:

3

func MustZero

func MustZero[T comparable](value T)

MustZero panics if the given value is not equal to the generic zero. This is also useful to assert a nil error.

Example
MustZero(HelloError(false))

func Try

func Try(f func() error, err *error)

Try is best used with defer and an according function pointer, which is evaluated when the defer runs the try. It works best with io.Closer. Hopefully, this will get solved at the language level one day.

Example
fn := func() (err error) {
	defer Try(Close, &err)

	return nil
}
Output:

close failed

Types

type Opt

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

Opt is introduced because range over func can only represent at most 2 arguments. Processing a (T, ok, error) becomes impossible. Also, it is not correct to always use pointers for modelling or to use hidden error types for clear optional cases where an absent thing is never an error by definition. This also helps for performance edge cases, where you can technically express that a value is really just a value and does not escape.

It sports also a non-nesting custom JSON serialization, which just uses NULL as representation. Note that if T is a pointer type, the Option becomes invalid after unmarshalling because a valid nil pointer cannot be distinguished from an invalid nil pointer in JSON, but you likely should not model your domain that way anyway.

If you already have a pointer, just use its zero value which is nil and not Option. The zero value is safe to use.

func None

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

None is only for better readability and equal to the zero value of Option.

func Some

func Some[T any](v T) Opt[T]

Some is a factory to create a valid option.

func (Opt[T]) All

func (o Opt[T]) All() iter.Seq[T]

All allows iteration over the possibly contained value.

func (Opt[T]) IsNone

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

func (Opt[T]) IsSome

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

func (Opt[T]) IsZero

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

func (Opt[T]) MarshalJSON

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

func (*Opt[T]) UnmarshalJSON

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

func (Opt[T]) Unwrap

func (o Opt[T]) Unwrap() T

Unwrap makes the assertion that the Option is valid and otherwise panics. Such panic is always a programming error.

func (Opt[T]) UnwrapOr

func (o Opt[T]) UnwrapOr(v T) T

UnwrapOr returns either the contained value or the eagly evaluated given value. See also Opt.UnwrapOrElse.

func (Opt[T]) UnwrapOrElse

func (o Opt[T]) UnwrapOrElse(fn func() T) T

UnwrapOrElse returns either the contained value or evaluates the given func to return an alternative.

type Ptr

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

Ptr is an immutable wrapper type around a pointer to a value of T. It returns always the T and never the pointer to T to express immutability. This is similar to Opt[T] but has an entire different memory characteristic. The optional implementation uses a flag and value type of T which requires the additional bool and the entire T. If you have a lot of options, e.g. in a nested struct, this will cause a lot of waste, but usually it lands on the stack.

This Ptr implementation only keeps a pointer (usually 8 byte) to the heap escaped value but once given to the caller, the wrapped pointer cannot be mutated and only access to the dereferenced value is possible which creates a copy of T.

func Pointer

func Pointer[T any](v *T) Ptr[T]

Pointer is a factory to take the ownership of the given pointer.

func (Ptr[T]) All

func (o Ptr[T]) All() iter.Seq[T]

All allows iteration over the possibly contained value.

func (Ptr[T]) IsNone

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

func (Ptr[T]) IsSome

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

func (Ptr[T]) IsZero

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

func (Ptr[T]) MarshalJSON

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

func (*Ptr[T]) UnmarshalJSON

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

func (Ptr[T]) Unwrap

func (o Ptr[T]) Unwrap() T

Unwrap makes the assertion that the Option is valid and otherwise panics. Such panic is always a programming error.

func (Ptr[T]) UnwrapOr

func (o Ptr[T]) UnwrapOr(v T) T

UnwrapOr returns either the contained value or the eagly evaluated given value. See also Opt.UnwrapOrElse.

func (Ptr[T]) UnwrapOrElse

func (o Ptr[T]) UnwrapOrElse(fn func() T) T

UnwrapOrElse returns either the contained value or evaluates the given func to return an alternative.

Jump to

Keyboard shortcuts

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