Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Go function to return pointer of any type?

Tags:

pointers

go

Using Go 1.16

Currently no plan to use Go 1.18


We have a scenario of using below struct:

type X struct {
    M1 *int64
    M2 *string
    M3 *[]string
}

For below code:

package main

import (
    "fmt"
    "time"
)

type X struct {
    M1 *int64
    M2 *string
    M3 *[]string
}

func main() {

    slice := []string{"abc", "def"}

    getIntPointer := func(i int64) *int64 {

        return &i
    }

    getStringPointer := func(s string) *string {
        return &s
    }

    getStringSlicePointer := func(slice []string) *[]string {
        return &slice
    }

    value := X{
        M1: getIntPointer(time.Now().Unix()),
        M2: getStringPointer("00000000-0000-0000-0000-000000000000"),
        M3: getStringSlicePointer(slice),
    }
    fmt.Println(value)
}

This function should expect input argument of any type (not just the above three types). Goal is to initialize value.

Can a single function return a pointer of any type?

like image 432
overexchange Avatar asked Sep 15 '25 16:09

overexchange


1 Answers

Type of the M1, M2 and M3 fields are different, so without generics (Go 1.18) you can't have a function that have multiple return types, assignable to these different fields.

Before showing what's possible with older versions of Go (pre 1.18), let me tell you that I would not use them in production. They are inconvenient and slow (due to reflection). I would write helpers for different types and use those, or use a library that already has them, e.g. github.com/icza/gox (package gox) (disclosure: I'm the author).

And if Go 1.18 is allowed, I would use the generic solution shown at the end.

set() using reflection

What you can do is write a helper that gets the address of a field and the value you want to set for it (more precisely a pointer to it), and using reflection this is doable.

This is how this helper could look like:

func set(p, v interface{}) {
    rv := reflect.ValueOf(v)
    rp := reflect.New(rv.Type())
    rp.Elem().Set(rv)
    reflect.ValueOf(p).Elem().Set(rp)
}

Using it:

slice := []string{"abc", "def"}

var value X
set(&value.M1, time.Now().Unix())
set(&value.M2, "00000000-0000-0000-0000-000000000000")
set(&value.M3, slice)

fmt.Println(*value.M1, *value.M2, *value.M3)

This will output (try it on the Go Playground):

1257894000 00000000-0000-0000-0000-000000000000 [abc def]

ptr() using reflection and type assertion

Another, similarly inconvenient solution would be to write a single helper that returns a pointer to its argument using reflection:

func ptr(v interface{}) interface{} {
    rv := reflect.ValueOf(v)
    rp := reflect.New(rv.Type())
    rp.Elem().Set(rv)
    return rp.Interface()
}

The problem with this is that you have to use type assertion on the result:

value := X{
    M1: ptr(time.Now().Unix()).(*int64),
    M2: ptr("00000000-0000-0000-0000-000000000000").(*string),
    M3: ptr(slice).(*[]string),
}

Try this one on the Go Playground.

Go 1.18 generic solution

If Go 1.18 will be allowed for you, a single helper is enough that uses no reflection:

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

And no type assertion is needed to use it:

value := X{
    M1: Ptr(time.Now().Unix()),
    M2: Ptr("00000000-0000-0000-0000-000000000000"),
    M3: Ptr(slice),
}

Try this one on the Go Playground.

See related: How do I do a literal *int64 in Go?

like image 141
icza Avatar answered Sep 18 '25 09:09

icza