Go函数返回任何类型的指针?

ove*_*nge 4 pointers go

使用Go 1.16

目前没有计划使用Go 1.18


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

type X struct {
    M1 *int64
    M2 *string
    M3 *[]string
}
Run Code Online (Sandbox Code Playgroud)

对于下面的代码:

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)
}
Run Code Online (Sandbox Code Playgroud)

该函数应该期望任何类型的输入参数(不仅仅是上述三种类型)。目标是初始化value.

单个函数可以返回任意类型的指针吗?

icz*_*cza 7

M1M2和字段的类型M3不同,因此如果没有泛型(Go 1.18),您就无法拥有具有多个返回类型、可分配给这些不同字段的函数。

在展示旧版本 Go(1.18 之前)的可能性之前,让我告诉你我不会在生产中使用它们。它们不方便且缓慢(由于反射)。我会为不同类型编写帮助程序并使用它们,或者使用已经拥有它们的库,例如github.com/icza/gox(package 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)
}
Run Code Online (Sandbox Code Playgroud)

使用它:

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)
Run Code Online (Sandbox Code Playgroud)

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

1257894000 00000000-0000-0000-0000-000000000000 [abc def]
Run Code Online (Sandbox Code Playgroud)

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

另一个同样不方便的解决方案是编写一个帮助器,使用反射返回指向其参数的指针:

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

这样做的问题是您必须对结果使用类型断言:

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

在Go Playground上试试这个。

Go 1.18通用解决方案

如果 Go 1.18 允许你使用,那么一个不使用反射的助手就足够了:

func Ptr[T any](t T) *T {
    return &t
}
Run Code Online (Sandbox Code Playgroud)

使用它不需要类型断言:

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

在Go Playground上试试这个。

请参阅相关内容:如何在 Go 中执行文字 *int64?