Go function to return pointer of any type?

huangapple go评论81阅读模式
英文:

Go function to return pointer of any type?

问题

使用Go 1.16

目前没有使用Go 1.18的计划


我们有一个使用以下结构的场景:

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

对于下面的代码:

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)
}

这个函数应该接受任何类型的输入参数(不仅仅是上面的三种类型)。目标是初始化value

一个单独的函数能返回任何类型的指针吗?

英文:

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?

答案1

得分: 6

M1M2M3字段的类型是不同的,所以在没有泛型(Go 1.18之前)的情况下,你不能有一个具有多个返回类型的函数,可以赋值给这些不同的字段。

在展示旧版本Go(1.18之前)的可能性之前,我要告诉你我不会在生产中使用它们。它们不方便且速度较慢(由于反射)。我会为不同的类型编写辅助函数并使用它们,或者使用已经存在的库,例如github.com/icza/gox(包gox)(声明:我是作者)。

如果允许使用Go 1.18,我会使用在最后展示的泛型解决方案。

使用反射的set()函数

你可以编写一个辅助函数,该函数获取字段的地址和要设置的值(更准确地说是指向它的指针),然后使用反射来实现。

这是这个辅助函数的样子:

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

使用它:

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)

这将输出(在Go Playground上尝试):

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

使用反射和类型断言的ptr()函数

另一种同样不方便的解决方案是编写一个单独的辅助函数,使用反射返回其参数的指针:

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

问题是你必须对结果使用类型断言:

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

Go Playground上尝试这个。

Go 1.18的泛型解决方案

如果允许使用Go 1.18,只需要一个不使用反射的辅助函数:

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

使用它时不需要类型断言:

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

Go Playground上尝试这个。

相关链接:https://stackoverflow.com/questions/30716354/how-do-i-do-a-literal-int64-in-go/30716481#30716481

英文:

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: https://stackoverflow.com/questions/30716354/how-do-i-do-a-literal-int64-in-go/30716481#30716481

huangapple
  • 本文由 发表于 2022年4月7日 04:22:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/71773179.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定