【发布时间】:2021-10-14 15:09:03
【问题描述】:
现在golang/go:master 上提供了类型参数,我决定试一试。我似乎遇到了在Type Parameters Proposal 中找不到的限制。 (或者我一定错过了)。
我想编写一个函数,它返回具有接口类型约束的泛型类型值的切片。如果传递的类型是一个带有指针接收器的实现,我们如何实例化它?
type SetGetter[V any] interface {
Set(V)
Get() V
}
// SetGetterSlice turns a slice of type V into a slice of type T,
// with T.Set() called for each entry in values.
func SetGetterSlice[V any, T SetGetter[V]](values []V) []T {
out := make([]T, len(values))
for i, v := range values {
out[i].Set(v) // panic if T has pointer receiver!
}
return out
}
当以*Count 类型为T 调用上述SetGetterSlice() 函数时,此代码将在调用Set(v) 时出现恐慌。 (Go2go playground) 毫不奇怪,因为基本上代码创建了nil 指针的片段:
// Count implements SetGetter interface
type Count struct {
x int
}
func (c *Count) Set(x int) { c.x = x }
func (c *Count) Get() int { return c.x }
func main() {
ints := []int{1, 2, 3, 4, 5}
sgs := SetGetterSlice[int, *Count](ints)
for _, s := range sgs {
fmt.Println(s.Get())
}
}
同一个问题的变种
这个想法行不通,我似乎找不到任何简单的方法来实例化指向的值。
-
out[i] = new(T)将产生一个compile failure,因为它返回一个*T,类型检查器希望在其中看到T。 - 调用
*new(T),编译但会产生相同的runtime panic,因为在这种情况下new(T)返回**Count,指向Count的指针仍然是nil。 - 将返回类型更改为指向
T的指针切片将导致compile failure:
func SetGetterSlice[V any, T SetGetter[V]](values []V) []*T {
out := make([]*T, len(values))
for i, v := range values {
out[i] = new(T)
out[i].Set(v) // panic if T has pointer receiver
}
return out
}
func main() {
ints := []int{1, 2, 3, 4, 5}
SetGetterSlice[int, Count](ints)
// Count does not satisfy SetGetter[V]: wrong method signature
}
解决方法
到目前为止,我发现的唯一解决方案是要求将 constructor function 传递给通用函数。但这只是感觉不对,而且有点乏味。如果func F(T interface{})() []T 是完全有效的语法,为什么还要这样做?
func SetGetterSlice[V any, T SetGetter[V]](values []V, constructor func() T) []T {
out := make([]T, len(values))
for i, v := range values {
out[i] = constructor()
out[i].Set(v)
}
return out
}
// ...
func main() {
ints := []int{1, 2, 3, 4, 5}
SetGetterSlice[int, *Count](ints, func() *Count { return new(Count) })
}
总结
我的问题,按优先顺序排列:
- 我是否忽略了一些明显的东西?
- 这是 Go 中泛型的限制吗?这已经是最好的了?
- 这个限制是已知的还是我应该在 Go 项目中提出问题?
【问题讨论】: