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