我正在寻找一种方法来声明Go语言泛型约束中类型参数之间的类型兼容性。
更具体地说,我需要说明一些类型T
与另一个类型U
兼容,例如,T
是一个指向实现接口U
的结构体的指针。
下面是我想完成的一个具体例子:
- 注意:请不要用实现“数组前置”的替代方法来回答。我只是将它作为我希望解决的问题的一个具体应用。专注于具体的示例会偏离主题。*
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
}
可以调用上面的函数将给定类型T
的元素追加到相同类型的数组中,因此the code below可以正常工作:
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)})
}
如果数组类型与要添加的元素不同(例如,由元素类型实现的接口),则the code fails to compile(如预期)与type *Foo of &Foo{…} does not match inferred type Base for T
:
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)})
}
直观的解决方案是修改Prepend
的类型参数,使array
和values
具有不同但兼容的类型,这是我不知道如何在Go语言中表达的部分。
例如,the code below不能正常工作(正如预期的那样),因为array
和values
的类型是相互独立的;类似的代码可以用于C++模板,因为兼容性是在模板示例化之后**验证的(类似于duck类型),Go编译器给出错误invalid argument: arguments to copy result (variable of type []A) and values (variable of type []T) have different element types A and 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
}
I've tried making类型T
与A
兼容,约束为~A
,但是Go语言不喜欢类型参数作为约束的类型,给出了错误type in term ~A cannot be a type parameter
:
func Prepend[A any, T ~A](array []A, values ...T) []A {
在不诉诸反射的情况下,将这种类型兼容性声明为泛型约束的正确方法是什么?
1条答案
按热度按时间de90aj5v1#
这是Go语言类型参数推理的一个局限性,类型参数推理是指在没有显式定义类型参数的情况下,自动插入类型参数的系统,尝试显式地插入类型参数,你会发现它是有效的,例如:
您还可以尝试将
*Foo
值显式转换为Base
接口。例如:解释
首先,您应该记住,类型参数的“正确”使用是始终显式包含它们。省略类型参数列表的选项被认为是“最好拥有”,但不打算涵盖所有用例。
来自博客文章An Introduction To Generics:
类型推断在实践中
类型推理工作的确切细节是复杂的,但使用它并不复杂:类型推理要么成功要么失败。2如果成功,类型参数可以省略,调用泛型函数看起来和调用普通函数没有什么不同。3如果类型推理失败,编译器将给予一个错误信息,在这种情况下,我们可以只提供必要的类型参数。
在向语言中添加类型推断时,我们尝试在推断能力和复杂性之间取得平衡。我们希望确保当编译器推断类型时,这些类型永远不会令人惊讶。我们尝试小心地犯错误,在未能推断出类型的一侧而不是推断出错误的类型的一侧。我们可能没有完全正确。我们可能会在未来的版本中继续完善它。其结果将是更多的程序可以不用显式类型参数来编写。今天不需要类型参数的程序明天也不需要。
换句话说,类型推断可能会随着时间的推移而改进,但您应该预料到它是有限的。
在这种情况下:
对于编译器来说,通过替换
T = *Foo
来匹配[]*Foo
和*Foo
的参数类型与模式[]T
和...T
的匹配是相对简单的。那么,为什么你最初给出的简单解决方案行不通呢?
要使
[]Base
和*Foo
与模式[]T
和...T
匹配,仅仅替换T = *Foo
或T = Base
没有提供明显的匹配。您必须应用*Foo
可赋值给类型Base
的规则来查看T = Base
是否工作。显然,推理系统不“Don“不要多花一点力气去弄清楚这一点,所以在这里失败了。