Home > Back-end >  How do you write a generic Go function that allows pointers to multiple primitive types?
How do you write a generic Go function that allows pointers to multiple primitive types?

Time:03-26

I'm trying to use Go generics to write a function to cut down some of the boilerplate if/else blocks in our code. I came up with something that works for a single type parameter as follows:

func valueOrNil[T *int](value T) any {
    if value == nil {
        return nil
    }
    return *value
}

While this works fine, it's not really useful since it only allows *int, and I want this code to work with any primitive type. I tried to extend that to support a second type, as follows:

func valueOrNil[T *int | *uint](value T) any {
    if value == nil {
        return nil
    }
    return *value
}

However, this variation fails with a compiler error:

invalid operation: pointers of value (variable of type T constrained by *int|*uint) must have identical base types

Can anyone spot what I'm doing wrong here, or is something like this just "not supported"?

CodePudding user response:

The trouble seems to be that you're trying to be generic over a pointer to the type rather than the type itself. If we move the pointer to be on the parameter itself rather than the type parameter, it works.

Explanation is below, but here is the working code:

func valueOrNil[T ~int | ~uint](value *T) T {
    if value == nil {
        var zero T
        return zero
    }
    return *value
}

So instead of this (which doesn't work):

func valueOrNil[T *int | *uint](value T) any

You could do this:

func valueOrNil[T int | uint](value *T) any

However, you may want to take it a step further and handle underlying types:

func valueOrNil[T ~int | ~uint](value *T) any

This would allow a custom type to be used with the function:

type Thing int

var thing Thing
println(valueOrNil(thing))

Another facet you may want to consider is being generic over the return type as well. You can do so using the same T parameter.

For example:

func valueOrNil([T ~int | ~uint](value *T) T

But this means you would need to change part of the implementation. Instead of this:

if value == nil {
    return nil
}

You could do something like this:

if value == nil {
    var zero T
    return zero
}

CodePudding user response:

The answer I accepted from Miquella (thanks for the insights; I learned something about the usage of ~, too) made me realize that I don't actually need to specify a specific type once I move the * onto the parameter type, so this is what I ended up with:

func valueOrNil[T any](value *T) any {
    if value == nil {
        return nil
    }
    return *value
}
  • Related