问题
假设有如下场景,我们需要给两个不同的struct实现Equal
方法
1 | type Data1 struct { |
现在我们想定义一个通用接口Equaler
,并让两个类型去实现它
1 | type Equaler interface { |
但这个代码不能通过编译
原因
对于*Data1
的Equal
方法来说,参数类型为*Data1
,并不是接口所要求的Equaler
。在golang中参数中的 *Data1
类型,并不会自动promote为Equaler
接口类型。如果想要让*Data1
实现接口 Equaler
,需要对它的Equal
方法进行改写
1 | type Data1 struct { |
因为任意实现了Equaler接口的类型都能被当作参数传入Equal
方法,所以需要在运行时对Equaler
接口进行强制转化,确保它是 *Data1
类型
对于另一些语言来说,这一类型限制在编译期就能完成,以rust为例
1 | trait Equaler { |
在rust中,可以通过Self
指代当前实现trait的具体类型
,对于Data1
的equal
方法来说,Self
是Data1
,对于Data2
的equal
方法来说,Self
则是Data2
得益于Self
这一概念,我们不需要在运行时去做类型转换也能实现和go代码相同的效果
泛型支持
在go1.18泛型的加持下,可以对上述代码进行修改
1 | type Data1 struct { |
可以这么使用
1 | func IsEqual[T Equaler[T]](a, b T) bool { |
借助类型推导,使用时候也能不用显式指定泛型参数的类型
1 | fmt.Println(IsEqual(d1, d2)) //true |
这样我们就得到了一个支持比较任意两个实现Equaler[T]
接口的参数是否相等的 IsEqual
函数,且一切对于类型的限制和检查都是在编译期完成的
所以虽说IsEqual
代码实现看起来可读性较差,但至少它能实现一切我们想要的功能
在这个issue中,有对golang是否需要引入Self
的概念进行了讨论,但看起来官方还是倾向于使用泛型去达到这一效果•᷄ ᯅ •᷅