Go泛型:如何声明与另一类型参数兼容的类型参数
在Go泛型约束中声明类型参数的兼容性问题
我希望在Go泛型约束中声明类型参数之间的类型兼容性:指定类型T与另一类型U兼容,例如T是实现了接口U的结构体指针。以下以Prepend函数作为具体示例:
注意:请勿提供实现“数组前置”的替代方法,我仅将其作为问题的具体应用示例,聚焦示例会偏离讨论主题。
原始可运行的同类型前置函数
原始函数要求数组和待添加元素为同一类型,可正常运行:
func Prepend[T any](array []T, values ...T) []T { if len(values) < 1 { return array } result := make([]T, len(values) + len(array)) copy(result, values) copy(result[len(values):], array) return result }
使用示例:
type Foo struct{ x int } func (self *Foo) String() string { return fmt.Sprintf("foo#%d", self.x) } func grow(array []*Foo) []*Foo { return Prepend(array, &Foo{x: len(array)}) }
不同类型时的编译错误
当数组元素为接口类型,待添加元素为实现该接口的结构体指针时,代码编译失败:
type Base interface { fmt.Stringer } type Foo struct{ x int } func (self *Foo) String() string { return fmt.Sprintf("foo#%d", self.x) } func grow(array []Base) []Base { return Prepend(array, &Foo{x: len(array)}) }
报错信息:
type *Foo of &Foo{…} does not match inferred type Base for T
尝试的错误解决方案
独立类型参数的写法
尝试将数组和元素设为独立类型参数,但因切片类型不兼容导致编译错误:
func Prepend[A any, T any](array []A, values ...T) []A { if len(values) < 1 { return array } result := make([]A, len(values) + len(array)) copy(result, values) copy(result[len(values):], array) return result }
报错信息:
invalid argument: arguments to copy result (variable of type []A) and values (variable of type []T) have different element types A and T
使用~A约束的写法
尝试用~A约束T与A兼容,但Go不允许将类型参数用作约束类型:
func Prepend[A any, T ~A](array []A, values ...T) []A { // ... }
报错信息:
type in term ~A cannot be a type parameter
正确的解决方案
要实现T与A的兼容性(T可赋值给A),可以通过将T的约束指定为A来声明,同时由于Go不支持切片协变,需要手动将T类型的元素转换为A类型:
func Prepend[A any, T A](array []A, values ...T) []A { if len(values) == 0 { return array } result := make([]A, len(values)+len(array)) // 逐个转换T类型元素为A类型 for i, v := range values { result[i] = v } copy(result[len(values):], array) return result }
约束说明
- 当
A是接口类型时,T A表示T必须实现A接口,此时T类型的值可以直接赋值给A类型变量 - 当
A是具体类型时,T A表示T必须是A类型(或其底层类型为A的自定义类型,此时可改用T ~A,但A必须是具体类型,不能是类型参数)
这样修改后,之前的grow函数即可正常编译运行。
内容的提问来源于stack exchange,提问作者bst
相关产品推荐
相关产品推荐

