如何使用泛型 Go 实例化类型参数的非零指针?

2024年 2月 11日 68.4k 0

如何使用泛型 go 实例化类型参数的非零指针?

php小编西瓜将为您介绍如何在Go语言中使用泛型实例化类型参数的非零指针。在Go语言中,泛型是一种强大的特性,可以增加代码的灵活性和重用性。当我们需要在泛型函数或方法中实例化一个非零指针时,可以使用类型断言和反射来实现。通过使用这些技术,我们可以在运行时根据类型参数的具体类型来创建一个非零指针实例,从而实现泛型的灵活性和通用性。下面我们来详细了解一下具体的实现方法。

问题内容

现在 golang/go:master 上提供了类型参数,我决定尝试一下。看来我遇到了在类型参数提案中找不到的限制。 (或者我一定错过了)。

我想编写一个函数,它返回带有接口类型约束的泛型类型值的切片。如果传递的类型是带有指针接收器的实现,我们如何实例化它?

type SetGetter[V any] interface {
Set(V)
Get() V
}

// SetGetterSlice turns a slice of type V into a slice of type T,
// with T.Set() called for each entry in values.
func SetGetterSlice[V any, T SetGetter[V]](values []V) []T {
out := make([]T, len(values))

for i, v := range values {
out[i].Set(v) // panic if T has pointer receiver!
}

return out
}

登录后复制

当使用 *Count 类型作为 T 调用上述 SetGetterSlice() 函数时,此代码将在调用 Set(v) 时出现混乱。 (Go2go 游乐场)毫不奇怪,因为基本上代码创建了 nil 指针的切片:

// Count implements SetGetter interface
type Count struct {
x int
}

func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int { return c.x }

func main() {
ints := []int{1, 2, 3, 4, 5}

sgs := SetGetterSlice[int, *Count](ints)

for _, s := range sgs {
fmt.Println(s.Get())
}
}

登录后复制

同一问题的变体

这个想法行不通,我似乎找不到任何简单的方法来实例化指向的值。

  • out[i] = new(T) 将导致编译失败,因为它返回 *T,其中类型检查器希望查看 T
  • 调用 *new(T) 进行编译,但会导致相同的运行时恐慌,因为 new(T) 返回 **Count 在这种情况下,其中指向 Count 的指针仍然是 nil
  • 将返回类型更改为指向 T 的指针片段将导致编译失败:
  • func SetGetterSlice[V any, T SetGetter[V]](values []V) []*T {
    out := make([]*T, len(values))

    for i, v := range values {
    out[i] = new(T)
    out[i].Set(v) // panic if T has pointer receiver
    }

    return out
    }

    func main() {
    ints := []int{1, 2, 3, 4, 5}

    SetGetterSlice[int, Count](ints)
    // Count does not satisfy SetGetter[V]: wrong method signature
    }

    登录后复制

    解决方法

    到目前为止我发现的唯一解决方案是要求将构造函数传递给泛型函数。但这感觉不对,而且有点乏味。如果 func F(T interface{})() []T 是完全有效的语法,为什么需要这样做?

    func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T {
    out := make([]T, len(values))

    for i, v := range values {
    out[i] = constructor()
    out[i].Set(v)
    }

    return out
    }

    // ...
    func main() {
    ints := []int{1, 2, 3, 4, 5}

    SetGetterSlice[int, *Count](ints, func() *Count { return new(Count) })
    }

    登录后复制

    摘要

    我的问题(按优先顺序排列):

  • 我是否忽略了一些显而易见的事情?
  • 这是 Go 中泛型的限制吗?这已经是最好的了吗?
  • 此限制是否已知,或者我应该在 Go 项目中提出问题吗?
  • 解决方法

    基本上,您必须向约束添加一个类型参数,以使 T 可转换为其指针类型。在最基本的形式中,该技术如下所示(带有匿名约束):

    func Foo[T any, PT interface { *T; M() }]() {
    p := PT(new(T))
    p.M() // calling method on non-nil pointer
    }

    登录后复制

    游乐场:https://www.php.cn/link/24aef8cb3281a2422a59b51659f1ad2e

    分步解决方案

    您的约束 SetGetter 已经声明了类型参数 V,因此我们稍微修改上面的示例:

    // V is your original type param
    // T is the additional helper param
    type SetGetter[V any, T any] interface {
    Set(V)
    Get() V
    *T
    }

    登录后复制

    然后定义 SetGetterSlice 函数,其类型参数为 T any,其目的只是实例化约束 SetGetter

    然后您就可以将表达式 &out[i] 转换为指针类型,并成功调用指针接收器上的方法:

    // T is the type with methods with pointer receiver
    // PT is the SetGetter constraint with *T
    func SetGetterSlice[V any, T any, PT SetGetter[V, T]](values []V) []T {
    out := make([]T, len(values))

    for i, v := range values {
    // out[i] has type T
    // &out[i] has type *T
    // PT constraint includes *T
    p := PT(&out[i]) // valid conversion!
    p.Set(v) // calling with non-nil pointer receiver
    }

    return out
    }

    登录后复制

    完整程序:

    CFE57E536C89530D9A8C38E10967A10D

    这变得更加冗长,因为 SetGetterSlice 现在需要三个类型参数:原始 V 加上 T (带有指针接收器的类型)和 PT (新约束)。然而,当您调用该函数时,您可以省略第三个 - 通过类型推断,实例化 PT SetGetter[V,T] 所需的类型参数 VT 都是已知的:

    SetGetterSlice[int, Count](ints)

    登录后复制

    游乐场:https://www.php.cn/link/6b061fc28f7473418a006dfa832708b1

    以上就是如何使用泛型 Go 实例化类型参数的非零指针?的详细内容,更多请关注每日运维网(www.mryunwei.com)其它相关文章!

    相关文章

    JavaScript2024新功能:Object.groupBy、正则表达式v标志
    PHP trim 函数对多字节字符的使用和限制
    新函数 json_validate() 、randomizer 类扩展…20 个PHP 8.3 新特性全面解析
    使用HTMX为WordPress增效:如何在不使用复杂框架的情况下增强平台功能
    为React 19做准备:WordPress 6.6用户指南
    如何删除WordPress中的所有评论

    发布评论