Generic pointer of value with Go

· 264 words · 2 minute read

I’ve been writing a decent amount of Go during the past 8 years. While I like many things about Go, some of the design choices result in repetitive or longer than necessary code.

Using Go, you often have to make choices between value types and pointer types. And libraries / APIs have made choices as well, and have to be supplied with correctly typed values. One group of things in particular that I’ve run into, are functions that take pointers, but you usually have constants or other unaddressable values at hand:

// foo(*string)

foo(&"foobar") // invalid
foo(&myConst) // invalid
foo(&myMap["baz"]) // invalid
foo(&bar("baz")) // invalid

What you usually end up doing is assigning the value to a variable so that you can take the address:

bar := myConst
foo(&bar)

This seems to be a common issue for more people than me. Looking at the Stripe Go API or Google Protobuf API, we have:

  • func Bool(v bool) *bool
  • func BoolSlice(v bool) []*bool
  • func BoolValue(v *bool) bool
  • func Float64(v float64) *float64
  • func String(v string) *string

Which let you write:

foo(stripe.String(myConst))

Now that Go added generics in 1.18, we can reduce the duplication and add a generic versions of the functions:

func Ptr[T any](v T) *T {
	return &v
}

func PtrSlice[T any](v []T) []*T {
	out := make([]*T, len(v))
	for i := range v {
		out[i] = &v[i]
	}

	return out
}

func Value[T any](p *T) T {
	if p != nil {
		return *p
	}

	var v T
	return v
}

// usage
foo(Ptr(myConst))

You can play with the code on the Go playground