英文:
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
M1
、M2
和M3
字段的类型是不同的,所以在没有泛型(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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论