4 ms·
I code in Golang daily. Generics are awesome! Some simple examples: // returns a pointer to v func ToPointer[T any](v T) *T { return &v }
by tastysandwich 5y ago
I code in Golang daily. Generics are awesome!
Some simple examples:
// returns a pointer to v
func ToPointer[T any](v T) *T {
return &v
}
// returns a given default value if v is nil, otherwise v
func DefaultValue[T any](v *T, defaultVal T) T {
if v == nil {
return defaultVal
}
return *v
}
Although a quick grep shows I mainly use these when writing tests.
I hope they allow type aliases on generics soon.
in package a:
type ASomething[T any] struct {}
in package b:
type BSomething = a.ASomething // doesn't work
type BSomething[T any] = a.ASomething[T any] // doesn't work
I found a need for this recently when updating a bunch of code in a common package I have to use generics. The above type alias would have meant I didn't have to update any code that uses that common package - the transition to generics "under the hood" would have been seamless. Oh well, nothing sed can't handle.
- xigoi 5y agoNot knowing much about Go: Wouldn't ToPointer create a dangling pointer, because `v` is on the stack? Or is it automatically copied to the heap?
- cyphar 5y agoGo has escape detection, so any returned pointer to a stack allocated variable becomes heap allocated. The code posted is a fairly common way of taking a stack allocated primitive type and turning it into a heap allocated one.
- eru 5y agoOr coming from the opposite side: you can say that Go has escape analysis, so that it can detect when variables can safely be allocated on the stack. The stack is an implementation detail that's purely about optimization. (I don't know for sure whether it's safe to thing like this in Go. It's safe to think like this in most Lisps, OCaml or Haskell.)
- Dylan16807 5y agoI think that's mostly true, but Go is allowed to move variables so it could allocate on the stack in all cases if it wanted to.
- tsimionescu 5y agoGo is only allowed to move variables if it knows there are not pointers to them, so only the compiler can take this decision. The Go runtime is very much not allowed to move variables.
- Dylan16807 5y agoI don't think this is right. Every source I can find says that Go does not currently move heap variables, but it reserves the right to, and code that assumes no movement is wrong. I also found code that demonstrates stack variables being moved despite a pointer to them, but it's version dependent so I can't test it. When a variable is moved, then obviously pointers to it need to be updated, but that's not an intractable problem. Looking further, Go doesn't even provide an API for pinning objects right now[1]. You have to do weird workarounds to allocate static objects, or write code that assumes nothing on the heap moves and will explode if that ever changes. [1] https://github.com/golang/go/issues/46787 https://github.com/golang/go/issues/46787
- cyphar 5y agoThat's probably a better way of explaining the behaviour. My point was more that this pattern is a very common way of getting a pointer to a primitive type value (since you can't just do &5 in Go). It seems likely that Go would optimise this to just creating a stack allocated value.
- divan 5y agoCould you elaborate on why do you need your custom generic function to return pointer? Also, how 'ToPointer(v)' is better than writing just plain '&v'?
- 0x6877 5y agoSometimes you need to instantiate a struct, usually in a test, that contains a bunch of pointers to built in types. Say a struct representing a json request body with optional fields. You can’t just take a pointer to a string literal `&”foo”` and making a variable for everyone of these fields is cumbersome so you have a function that returns a pointer to the value it’s given. I’ve personally never needed it outside of tests.
- closeparen 5y agoIf you have e.g. a *string you can't populate it with &"hello world." You have to first assign the string to a variable and then take the address of that variable. This comes up very often in tests and is super inconvenient. Many people write TypeNamePtr functions so that you can call e.g. StringPtr("hello world") to express it in one line and without creating any names. Without generics you had to do this individually for every type.