前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >2023年9月26日 Go生态洞察:深入解析类型参数

2023年9月26日 Go生态洞察:深入解析类型参数

作者头像
猫头虎
发布2024-04-08 15:23:30
930
发布2024-04-08 15:23:30
举报

2023年9月26日 Go生态洞察:深入解析类型参数

🐾 摘要 大家好,我是猫头虎,今天我们要探讨的是Go语言中类型参数的构造和使用。通过深入分析slices.Clone函数,我们将理解类型参数在Go泛型中的作用和重要性。这不仅是一个技术深度话题,而且对于深入理解Go语言的泛型系统至关重要。

🐾 引言 在Go 1.18的引入泛型之后,类型参数成为了Go语言的一个新亮点。它们提供了编写更加灵活和可复用代码的能力。本文将通过Clone函数的例子,展示如何有效地使用类型参数来增强代码的通用性和灵活性。


🐾 正文内容

🐈 slices包函数签名

Clone函数非常简单:它可以复制任何类型的切片。

代码语言:javascript
复制
func Clone[S ~[]E, E any](s S) S {
    return append(s[:0:0], s...)
}

这个函数之所以有效,是因为对零容量的切片进行追加操作会分配一个新的底层数组。函数体实际上比函数签名更短,这部分是因为体本身简短,但也因为签名较长。接下来,我们将解释为什么签名是这样写的。

🐈 简单的克隆实现

我们首先编写一个简单的通用Clone函数。这并不是slices包中的那一个。我们希望接受任何元素类型的切片,并返回一个新切片。

代码语言:javascript
复制
func Clone1[E any](s []E) []E {
    // body omitted
}

泛型函数Clone1有一个类型参数E。它接受一个类型为E的切片参数s,并返回同类型的切片。这个签名对熟悉Go中泛型的人来说很直接。

然而,这里有一个问题。在Go中,命名的切片类型不常见,但人们确实会使用它们。

代码语言:javascript
复制
// MySlice 是一个具有特殊String方法的字符串切片。
type MySlice []string

// String 返回MySlice值的可打印版本。
func (s MySlice) String() string {
    return strings.Join(s, "+")
}

假设我们想要复制一个MySlice,然后获取其排序后的可打印版本。

代码语言:javascript
复制
func PrintSorted(ms MySlice) string {
    c := Clone1(ms)
    slices.Sort(c)
    return c.String() // 编译失败
}

不幸的是,这不起作用。编译器报告错误:

代码语言:javascript
复制
c.String undefined (type []string has no field or method String)
🐈 灵活的克隆实现

为了解决这个问题,我们必须编写一个版本的Clone,它返回与其参数相同的类型。如果我们做到了这一点,那么当我们用MySlice类型的值调用Clone时,它将返回MySlice类型的结果。

我们知道它应该是这样的:

代码语言:javascript
复制
func Clone2[S ?](s S) S // 无效

这个Clone2函数返回与其参数相同类型的值。

🐈 底层类型约束

如错误消息所示,答案是添加一个~

代码语言:javascript
复制
func Clone5[S ~[]E, E any](s S) S

再次强调,用[S []E, E any]这样的类型参数和约束表示,意味着S的类型参数可以是任何未命名的切片类型,但

不能是定义为切片字面量的命名类型。用[S ~[]E, E any],加上~,意味着S的类型参数可以是任何底层类型为切片类型的类型。

🐈 类型推断

现在我们已经解释了slices.Clone的签名,让我们看看如何通过类型推断简化对slices.Clone的使用。

代码语言:javascript
复制
func Clone[S ~[]E, E any](s S) S

调用slices.Clone时,将传递一个切片给参数s。类型推断将允许编译器推断出类型参数S是传递给Clone的切片的类型。然后,类型推断足够强大,可以看出E的类型参数是传递给S的类型参数的元素类型。

🐈 类型参数的解构

我们在这里使用的一般技术,即使用另一个类型参数E定义一个类型参数S,是一种在泛型函数签名中解构类型的方法。通过解构类型,我们可以命名并约束类型的所有方面。

例如,这是maps.Clone的签名:

代码语言:javascript
复制
func Clone[M ~map[K]V, K comparable, V any](m M) M

就像slices.Clone一样,我们使用一个类型参数来表示参数m的类型,然后使用另外两个类型参数KV来解构该类型。

maps.Clone中,我们将K约束为可比较的,因为这是映射键类型所必需的。我们可以根据喜好约束组成类型。

代码语言:javascript
复制
func WithStrings[S ~[]E, E interface { String() string }](s S) (S, []string)

这表示WithStrings的参数必须是一个切片类型,其元素类型具有String方法。

由于所有Go类型都可以从组成类型构建,我们总是可以使用类型参数来解构这些类型,并根据我们的喜好对它们进行约束。


🐾 总结

总的来说,类型参数在Go泛型中扮演着至关重要的角色。通过精心设计的函数签名和有效利用类型推断,我们可以编写更灵活、更通用的代码。希望这篇文章能帮助你更好地理解Go中的泛型。这篇文章由猫头虎的Go生态洞察专栏收录,更多详情请点击这里

关键点

描述

类型参数使用

使用类型参数构建灵活通用的函数

slices.Clone分析

分析Clone函数的类型参数和其用法

底层类型约束

理解底层类型的约束和它们的应用

类型推断

探索类型推断在泛型编程中的作用

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2024-04-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 2023年9月26日 Go生态洞察:深入解析类型参数
    • 🐾 正文内容
      • 🐈 slices包函数签名
      • 🐈 简单的克隆实现
      • 🐈 灵活的克隆实现
      • 🐈 底层类型约束
      • 🐈 类型推断
      • 🐈 类型参数的解构
    • 🐾 总结
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档