目录
问题内容
同一问题的变体
解决方法
摘要
首页 后端开发 Golang 如何使用泛型 Go 实例化类型参数的非零指针?

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

Feb 11, 2024 pm 05:36 PM
go语言 排列

如何使用泛型 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())
    }
}
登录后复制

同一问题的变体

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

  1. out[i] = new(T) 将导致编译失败,因为它返回 *T,其中类型检查器希望查看 T
  2. 调用 *new(T) 进行编译,但会导致相同的运行时恐慌,因为 new(T) 返回 **Count 在这种情况下,其中指向 Count 的指针仍然是 nil
  3. 将返回类型更改为指向 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) })
}
登录后复制

摘要

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

  1. 我是否忽略了一些显而易见的事情?
  2. 这是 Go 中泛型的限制吗?这已经是最好的了吗?
  3. 此限制是否已知,或者我应该在 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 加上 SetGetterSlice 现在需要三个类型参数:原始 V 加上 T (带有指针接收器的类型)和 PT (新约束)。然而,当您调用该函数时,您可以省略第三个 - 通过类型推断,实例化 PT SetGetter[V,T] 所需的类型参数 VT (带有指针接收器的类型)和 PT (新约束)。然而,当您调用该函数时,您可以省略第三个 - 通过类型推断,实例化 PT SetGetter[V,T] 所需的类型参数 V

都是已知的:

SetGetterSlice[int, Count](ints)
登录后复制
游乐场:https://www.php.cn/link/6b061fc28f7473418a006dfa832708b1🎜

以上是如何使用泛型 Go 实例化类型参数的非零指针?的详细内容。更多信息请关注PHP中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
4 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

Go语言中哪些库是由大公司开发或知名的开源项目提供的? Go语言中哪些库是由大公司开发或知名的开源项目提供的? Apr 02, 2025 pm 04:12 PM

Go语言中哪些库是大公司开发或知名开源项目?在使用Go语言进行编程时,开发者常常会遇到一些常见的需求,�...

c上标3下标5怎么算 c上标3下标5算法教程 c上标3下标5怎么算 c上标3下标5算法教程 Apr 03, 2025 pm 10:33 PM

C35 的计算本质上是组合数学,代表从 5 个元素中选择 3 个的组合数,其计算公式为 C53 = 5! / (3! * 2!),可通过循环避免直接计算阶乘以提高效率和避免溢出。另外,理解组合的本质和掌握高效的计算方法对于解决概率统计、密码学、算法设计等领域的许多问题至关重要。

GoLand中自定义结构体标签不显示怎么办? GoLand中自定义结构体标签不显示怎么办? Apr 02, 2025 pm 05:09 PM

GoLand中自定义结构体标签不显示怎么办?在使用GoLand进行Go语言开发时,很多开发者会遇到自定义结构体标签在�...

在Go语言中使用Redis Stream实现消息队列时,如何解决user_id类型转换问题? 在Go语言中使用Redis Stream实现消息队列时,如何解决user_id类型转换问题? Apr 02, 2025 pm 04:54 PM

Go语言中使用RedisStream实现消息队列时类型转换问题在使用Go语言与Redis...

网页批注如何实现Y轴位置的自适应布局? 网页批注如何实现Y轴位置的自适应布局? Apr 04, 2025 pm 11:30 PM

网页批注功能的Y轴位置自适应算法本文将探讨如何实现类似Word文档的批注功能,特别是如何处理批注之间的间�...

在使用Go语言和viper库时,为什么传递指针的指针是必要的? 在使用Go语言和viper库时,为什么传递指针的指针是必要的? Apr 02, 2025 pm 04:00 PM

Go指针语法及viper库使用中的寻址问题在使用Go语言进行编程时,理解指针的语法和使用方法至关重要,尤其是在...

Bootstrap图片居中需要用到flexbox吗 Bootstrap图片居中需要用到flexbox吗 Apr 07, 2025 am 09:06 AM

Bootstrap 图片居中方法多样,不一定要用 Flexbox。如果仅需水平居中,text-center 类即可;若需垂直或多元素居中,Flexbox 或 Grid 更合适。Flexbox 兼容性较差且可能增加复杂度,Grid 则更强大且学习成本较高。选择方法时应权衡利弊,并根据需求和偏好选择最适合的方法。

Go语言接口是鸭子类型吗?多态的实现机制究竟是什么? Go语言接口是鸭子类型吗?多态的实现机制究竟是什么? Apr 02, 2025 pm 02:48 PM

Go语言中的接口与多态:澄清常见误解许多Go语言初学者常常将“鸭子类型”和“多态”这两个概念与Go语言的接...

See all articles