Go泛型编程: 支持特例化?

一些编程语言如C++、Rust都是支持泛型特例化的,Go泛型支持吗?

所谓特例化(specialization)对泛型功能代码的扩展。比如对于泛型的函数,它的实现对于满足泛型参数的所有类型(type set)都是一样的,如果我们想对这些类型集(type set)中的一个,它的函数做特殊的实现,一些支持泛型特例化的语言是可以支持的,比如C++ template:

1
2
3
4
5
6
7
8
9
10
11
template <typename T>
void fun(T a)
{
cout << "template fun(): " << a << endl;
}
template <> // 对int型特例化
void fun(int a)
{
cout << "specialized template fun for int type: " << a << endl;
}

这里fun是一个函数模板,但是针对int类型,此函数有特殊的实现。

Rust也有类似的功能:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#![feature(min_specialization)]
struct Special
{
x : usize,
y : usize
}
trait MyTrait
{
fn myfunc(&self);
}
impl<T> MyTrait for T
{
default fn myfunc(&self) { println!("hi"); }
}
impl MyTrait for Special
{
fn myfunc(&self) { println!("I'm special"); }
}
fn main() {
let spe = Special{
x: 1,
y: 2,
};
spe.myfunc();
}

这里MyTrait针对通用类型T有一个默认实现,但是针对特定的类型Special,有一个特定的实现。

其它编程语言当前还不支持特例化,但是可以通过方法重载实现类似的功能,如typescript、C#等。
另外复杂的特例化还包括部分特例化的特性。

那么问题来了,Go的泛型(类型参数)是否支持特例化呢?我们先写个例子:

1
2
3
4
5
6
7
8
9
10
11
12
type List[T any] struct {
next *List[T]
value T
}
func (l *List[T]) Len() int {
return 0
}
func (l *List[string]) Length() int {
return 0
}

这里我们定义了一个泛型类型List[T any], 包括它的一个泛型方法Len() int,接下来我们尝试定义一个“特例化”的方法Length() int

编译一下,没问题,程序可以正常编译,难道Go泛型真的支持特例化吗?

我们再增加一个特例化泛型方法试试:

1
2
3
func (l *List[int]) Size() int {
return 0
}

这个时候再编译试试,编译出错:

1
cannot use 0 (untyped int constant) as int value in return statement

其实这个错误信息已经告诉我们了,这里的int并不是内建的整数类型,而是一个类型参数的名称,等价于我们常用的TKV。这里我们使用int作为类型参数的名称很有迷惑性。

所以答案也是很明确的,当前Go 1.18并不支持泛型特例化, 小心别掉到坑里了。