gu

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Mar 9, 2023 License: Unlicense Imports: 1 Imported by: 5

README

Generic Utilities

Go Reference Go codecov

GU is a collection of Generic Utility functions, using Type Parameters featured in Go 1.18 and later. I often found myself writing boilerplate code for slices, maps, poitners etc. Since 1.18 I started using generics in some of my repositories and found that some functions often are the same between projects. The repository is a collection of those (utiltity) functions.

Although the functions are pretty basic and almost don't justify putting them in a package, I share this code under the unlicense, with the purpose:

  • Make my own life easier when reusing boiler plate code;
  • So that others can easily use these utilities;
  • People who want to learn more about generics in Go can read the code;

Features

There is no logic in which order I'm adding features. Ussualy when I see repetative code that can be generalized, it is dropped in here. Which means that there might be other utilities that seem to be missing. Contributions are welcome.

Below features link to pkg.go.dev documentation where examples can be found.

Pointers
  • Ptr allows for getting a direct pointer. For example from fuction returns: t := gu.Ptr(time.Unix()) where t := &time.Unix() is illigal Go code.
  • Value safely returns a value through a pointer. When the pointer is nil, the zero value is returned without panic.
Slices
  • Transform a slice of any type into a slice of interface ([]T to []interface{}).
  • Assert a slice of interfaces to a slice of any type ([]interface{} to []T).
  • Transform slices of similar types that implement the Transformer interface.
Maps

Contributing

Open for Pull Requests.

  • In case of a bugfix, please clearly describe the issue and how to reproduce. Preferably a unit test that exposes the behaviour.
  • A new feature should be properly documented (godoc), added to REAMDE.md and fully unit tested. If the function seems to be abstract an example needs to be provided in the testfile (ExampleXxx() format)
  • All code needs to be go fmted

Please note the unlicense: you forfait all copyright when contributing to this repository.

Documentation

Overview

Package gu provides Generic Utilities for the Go programming language. These utilities are low in complexity, but I use them frequently in multiple projects. This package depends on type parameter support, available in Go 1.18 and later.

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

func AssertInterfaces

func AssertInterfaces[T any](is []interface{}) ([]T, error)

AssertInterfaces asserts all members of the passed slice of interfaces to the requested type and returs a slice of that type. A nil slice allong with an error is returned if one of the entries cannot be asserted to the destination type.

Example
interfaceSlice := []interface{}{"Hello", "World!"}
stringSlice, err := AssertInterfaces[string](interfaceSlice)
if err != nil {
	log.Fatal(err)
}

s := strings.Join(stringSlice, ", ")
fmt.Println(s)

interfaceSlice = []interface{}{1, 1.1, "foobar"}
intSlice, err := AssertInterfaces[int](interfaceSlice)
if err != nil {
	fmt.Println(err)
}

fmt.Println(intSlice)
Output:

Hello, World!
cannot assert float64 of value 1.1 to int at index 1
[]

func AssertInterfacesP

func AssertInterfacesP[T any](is []interface{}) []T

AssertInterfacesP is like AssertInterfaces, only that it does not check for succesfull assertion and lets the runtime panic if assertion fails. Usefull for inlining in cases where you are 100% sure of the concrete type of the passed slice.

Example
interfaceSlice := []interface{}{"Hello", "World!"}

s := strings.Join(AssertInterfacesP[string](interfaceSlice), ", ")
fmt.Println(s)
Output:

Hello, World!

func InterfaceSlice

func InterfaceSlice[T any](slice []T) []interface{}

InterfaceSlice transforms a slice of any type to a slice of interface{}. This can be usefull when you have a slice of concrete types that has to be passed to a function that takes a (variadic) slice of interface{}.

Example
stringSlice := []string{"Hello", ", ", "World", "!"}
// fmt.Print(stringSlice...): cannot use stringSlice (variable of type []string) as type []any in argument to fmt.Print
fmt.Print(InterfaceSlice(stringSlice)...)
Output:

Hello, World!

func MapCopy

func MapCopy[K comparable, V any](src map[K]V) map[K]V

MapCopy copies all the entries of src into a new map. Nil is returned when src is nil. Note that if V is a pointer or reference type, it is only shallow copied.

func MapCopyKeys

func MapCopyKeys[K comparable, V any](src map[K]V, keys ...K) map[K]V

MapCopyKeys copies the entries or src, identified by keys into a new map. Nil is returned when src is nil.

If no keys are provided and src is not nil, an empty non-nil map is returned.

Note that if V is a pointer or reference type, it is only shallow copied.

func MapEqual

func MapEqual[K, V comparable](a, b map[K]V) bool

MapEqual check if two maps have exactly the same content. If both maps are nil, they are considered equal. When a nil map is compared to an empty map, they are not considered equal.

func MapMerge added in v0.2.0

func MapMerge[K comparable, V any](src map[K]V, dst map[K]V)

MapMerge copies all entries from src in dst. Any pre-existing keys in dst are overwritten.

func Ptr

func Ptr[T any](value T) (pointer *T)

Ptr returns a pointer to the passed value. This helps where the ampersand can't be used directly, such as on constants or function returns.

Example
// Pointer of a string
// stringPointer := &"Hello world!": invalid operation: cannot take address of "Hello world!" (untyped string constant)
stringPointer := Ptr("Hello world!")
fmt.Printf("stringPointer is of type %T and points to value %v\n", stringPointer, *stringPointer)

// Constant
const i int64 = 22

// int64Pointer := &i: invalid operation: cannot take address of i (constant 22 of type int64)
int64Pointer := Ptr(i)
fmt.Printf("int64Pointer is of type %T and points to value %v\n", int64Pointer, *int64Pointer)

// Function return
// funcReturn := &fmt.Sprint(99): invalid operation: cannot take address of fmt.Sprint(99) (value of type string)
funcReturn := Ptr(fmt.Sprint(99))
fmt.Printf("funcReturn is of type %T and points to value %v\n", funcReturn, *funcReturn)
Output:

stringPointer is of type *string and points to value Hello world!
int64Pointer is of type *int64 and points to value 22
funcReturn is of type *string and points to value 99

func PtrCopy added in v0.3.0

func PtrCopy[T any](pointer *T) *T

PtrCopy copies a value behind pointer to a new pointer address. Returns nil when the input is nil.

func Transform

func Transform[A any, B any](as []A, transFunc func(A) B) []B

Transform a slice of type 'A' to a slice of type 'B', by calling transFunc for each entry.

Usefull when working with slices of different, but similar, struct types and you don't want to write the 'for' loops over and over again.

Example (Itoa)
in := []int{1, 2, 3, 4, 5}

out := Transform(in, strconv.Itoa)
fmt.Printf("out is of type %T and contains %v", out, out)
Output:

out is of type []string and contains [1 2 3 4 5]
Example (Struct)
type A struct {
	ID int32
	S  []string
}

type B struct {
	ID int64
	S  string
}

// define a tranformer function
transFunc := func(a A) B {
	return B{
		ID: int64(a.ID),
		S:  strings.Join(a.S, ", "),
	}
}

in := []A{
	{
		ID: 1,
		S:  []string{"Hello", "World!"},
	},
	{
		ID: 2,
		S:  []string{"foo", "bar"},
	},
	{
		ID: 3,
		S:  []string{"spanac"},
	},
}

// create the transformed slice
out := Transform(in, transFunc)

fmt.Printf("out is of type %T and contains %v", out, out)
Output:

out is of type []gu.B and contains [{1 Hello, World!} {2 foo, bar} {3 spanac}]

func TransformErr

func TransformErr[A any, B any](as []A, transFunc func(A) (B, error)) ([]B, error)

TransformErr is similar to Transform, but it uses a transFunc that can return an error.

TranformErr will fail on the first error returned and returns a wrapped error with index information, along with a partial slice from previous succesfull operations.

Example (Atoi)
in := []string{"1", "2", "3", "4", "5"}

out, err := TransformErr(in, strconv.Atoi)
if err != nil {
	panic(err)
}

fmt.Printf("out is of type %T and contains %v\n", out, out)

// this will cause an error
in = []string{"1", "2", "foo", "4", "5"}

out, err = TransformErr(in, strconv.Atoi)
if err != nil {
	fmt.Println(err)
}

fmt.Printf("out is of type %T and contains %v\n", out, out)
Output:

out is of type []int and contains [1 2 3 4 5]
transform index 2: strconv.Atoi: parsing "foo": invalid syntax
out is of type []int and contains [1 2]

func Value

func Value[T any](pointer *T) (value T)

Value return the value behind pointer. If the pointer is nil, the zero / empty value of T is returned. This helps to safely access variables where it does not matter of the program if they where nil or not, but you want to prevent a panic.

Common use case is fields in gerated structs from frameworks such as protobuf 2 or openapi 3.

Example
type document struct {
	ID          *int    `json:"id,omitempty"`
	Description *string `json:"description,omitempty"`
}

d := document{
	Description: Ptr("foobar"),
}

// this would panic, d.ID is nil
// fmt.Println(*d.ID, *d.Description)

// d.ID is nil, so a 0 is printed
fmt.Println(Value(d.ID), Value(d.Description))
Output:

0 foobar

Types

This section is empty.

Jump to

Keyboard shortcuts

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