同时具有接口和实现的 Golang 泛型

Golang generics with interface and implementation at same time

我正在尝试编写以下函数:

func Fill[X any](slice []*X){
   for i := range slice {
      slice[i] = new(X)
   }
}

xs := make([]*int, 10) // fill with nils
Fill(xs) // now fill with new(int)

这很好用,但是……如果我想使用一部分接口并提供具体类型?

func Fill[X, Y any](slice []X){
   for i := range slice {
      slice[i] = new(Y) // not work!
   }
}

xs := make([]sync.Locker, 10) // fill with nils
Fill[sync.Locker,sync.Mutex](xs) // ouch

我尝试了一些组合都没有成功,有什么办法还是go1.18不支持这样的关系?

当您将 XY 都约束为 any 时,您将失去所有 interface-implementor 关系。编译时唯一知道的是 XY 是不同的类型,你不能在函数体内将一个赋值给另一个。

使其编译的一种方法是使用显式断言:

func Fill[X, Y any](slice []X) {
    for i := range slice {
        slice[i] = any(*new(Y)).(X)
    }
}

但是如果 Y 没有真正实现 X 就像你的情况一样,这会引起恐慌,因为实现 sync.Locker.

的是 *sync.Mutex (指针类型)

此外,当 Y 用指针类型实例化时,您会丢失有关基类型的信息,因此零值,包括 *new(Y) 将是 nil,因此您与 make 相比并没有真正的基线改进(只是键入 nils 与 nil 接口)。

想做的是将 Y 限制为 X,就像 Fill[X any, Y X](slice []X) 一样,但这是不可能的,因为 1)类型参数不能用作约束; and/or 2) 约束不能直接嵌入类型参数。它还会像上面那样初始化 nils。

更好的解决方案是使用构造函数而不是第二个类型参数:

func main() {
    xs := make([]sync.Locker, 10)
    Fill(xs, func() sync.Locker { return &sync.Mutex{} })
}

func Fill[X any](slice []X, f func() X) {
    for i := range slice {
        slice[i] = f()
    }
}