Go 泛型函数中的 ~ 符号 的意义 -- 用于指定类型的底层类型

embedded/2024/10/19 3:27:00/

本文通过 slices.Clone 泛型函数介绍了 Go 是如何使用类型推断完成参数类型的解构。简单来说,如果第一个类型参数是一个复合类型,则可以通过第二、第三或更多的类型参数约束复杂类型中的类型参数,而类型推断则可以通过第一个参数推断出后续类型参数的实际类型。另外本文还说明为消除歧义而引入 ~ 符号,即用于指定类型的底层类型。

slices.Clone()函数原型

func Clone[S ~[]E, E any](s S) S {return append(s[:0:0], s...)
}

上面的  [S ~[]E, E any]  表示 S 可以是任意底层类型是 slice 的任意类型

如果上面的类型约束中没有 ~ 符号,如  [S []E, E any] 表示类型参数 S 可以是一个 slice 类型,但不能是一个 slice 的命名类型

如果 Go 语法中不使用 ~,那么 [S []E] 将会精确匹配到任意以 []E 作为底层类型的类型,这样我们就不得不定义 [S MySlice] 作为约束

Go 语法禁止 [S MySlice],或者说 [S MySlice] 只能匹配到 MySlice,但是对语言预定义的类型会造成困惑。作为预定义类型的 int,其底层类型依然是 int。我们希望 Go 语言能够能开发者提供精确匹配和定义约束底层类型为 int 的方式,如在程序中使用 [T ~int]。如果我们不使用 ~,[T int] 不能很好表明要使用底层类型为 int 语义。如果这么做了,那么 [T MySlice] 和 [T int] 的约束行为就会有歧义

我们可能会认为 [S MySlice] 匹配任意底层类型为 MySlice 的底层类型的类型,但这样会很困惑。

所以我们觉得使用 ~ 表明其底层类型会更好一些。

解构类型参数" style="margin-left:0;">解构类型参数

我们使用到的技术,即定义一个使用类型参数 E 的类型参数 S,是一种在泛型函数签名中解构类型的方式。通过解构类型,我们可以命名、约束类型的各个方面。

比如,maps.Clone 的签名如下:

func Clone[M ~map[K]V, K comparable, V any](m M) M

和 slices.Clone 一样,我们使用了类型参数 M 来约束参数 m,然后定义类型参数 K 和 V 用于解构类型。

在 maps.Clone 中,我们约束 K 必须是可比较型的,这与 map 的 key 的约束一致。也正因为这一特性,我们可以在开发过程中实现对复合类型的解构。

func WithStrings[S ~[]E, E interface { String() string }](s S) (S, []string)

上述示例中,我们要求 WithStrings 的参数类型必须是一个元素类型为带 String 方法的 slice。

因此,我们可以 Go 语言中在复合类型中使用类型推断来推断出其实际类型。

~ 使用示例 [S ~[]E, E comparable] 约束 使用示例

package mainimport ("fmt"
)func main() {var s1 = []int{1, 2, 7, 8, 1, 12, 16, 18, 20, 99}ret := Index(s1, 16)fmt.Printf("ret=%d\n", ret)
}// Index returns the index of the first occurence of v in s,
// or -1 if not present.
func Index[S ~[]E, E comparable](s S, v E) int {for i := range s {if v == s[i] {return i}}return -1
}


http://www.ppmy.cn/embedded/29070.html

相关文章

【ZZULIOJ】1092: 素数表(函数专题)(Java)

目录 题目描述 输入 输出 样例输入 Copy 样例输出 Copy code 题目描述 输入两个正整数m和n,输出m和n之间的所有素数。 要求程序定义一个prime()函数和一个main()函数,prime()函数判断一个整数n是否是素数,其余功能在main()函数中实现。…

鸿蒙OpenHarmony【标准系统 运行】(基于RK3568开发板)

运行 启动系统 烧录完成重启开发板后,系统将会自动启动。开发板附带的屏幕呈现以下界面,表明系统已运行成功。 图1 系统启动效果图 鸿蒙开发指导文档:gitee.com/li-shizhen-skin/harmony-os/blob/master/README.md点击或者复制转到。 运…

2024 java使用Graceful Response,告别自己去封装响应,可以接收数据异常,快看我这一篇,足够你用!

参考官网手册地址&#xff1a;快速入门 | Docs 一、导入依赖&#xff08;根据springboot查看对应依赖版本&#xff09; <!-- Graceful --><dependency><groupId>com.feiniaojin</groupId><artifactId>graceful-response</artifactId&g…

C++学习第十九课:STL动态数组`std::vector`的使用解析

C学习第十九课&#xff1a;STL动态数组std::vector的使用解析 std::vector是C标准模板库&#xff08;STL&#xff09;中的一个动态数组容器。它是一个序列容器&#xff0c;可以存储任意类型的元素&#xff0c;并允许在两个元素之间进行高效的插入和删除操作。std::vector是C程…

装饰器模式(模拟⼀个单点登录功能扩充)

目录 定义 模拟⼀个单点登录功能扩充 模拟Spring的HandlerInterceptor 模拟单点登录功能 代码实现 抽象类装饰⻆⾊ 装饰⻆⾊逻辑实现 测试验证 定义 装饰器的核⼼就是再不改原有类的基础上给类新增功能。不改变原有类&#xff0c;使⽤装饰器模式会是另外⼀种思路更为灵…

Java解决最后一个单词长度

Java解决最后一个单词长度 01 题目 给你一个字符串 s&#xff0c;由若干单词组成&#xff0c;单词前后用一些空格字符隔开。返回字符串中 最后一个 单词的长度。 单词 是指仅由字母组成、不包含任何空格字符的最大子字符串。 示例 1&#xff1a; 输入&#xff1a;s "Hel…

matlab图上 加文本注释

1、在图框中加 文本方法 —— text&#xff08;&#xff09;函数 2、使用箭头标注——annotation&#xff08;&#xff09;函数 xticks(1:0.2:2); str1 t^2; text(1.14,2.1,str1,Interpreter,latex); annotation("arrow",X,[0.3,0.5],Y,[0.6,0.4])参考example

DAO是什么?有什么用途?

DAO&#xff08;Decentralized Autonomous Organization&#xff0c;去中心化自治组织&#xff09;是一种基于区块链技术的组织形式&#xff0c;它没有中央管理层&#xff0c;而是通过智能合约和区块链上的代码来运作。DAO 的决策过程是透明的&#xff0c;通常由组织的成员通过…