实现接口的指针的泛型类型是什么?

What is the generic type for a pointer that implements an interface?

我有这样的界面

type A interface {
  SomeMethod()
}

我通过结构指针实现了这个接口:

type Aimpl struct {}

func (a *Aimpl) SomeMethod() {}

我有一个通用函数,它接受一个带有 A 参数的函数,如下所示:

func Handler[T A](callback func(result T)) {
  // Essentially what I'd like to do is result := &Aimpl{} (or whatever T is)
  callback(result)
}

我还应该补充一点,我不能修改 A 的定义(它来自图书馆)。我试过这个:

type MyA[T any] interface{
  A
  *T
}

但是这段代码给我一个错误:

func Handler[P any, T MyA[P]](callback func(result A)) {
    result := new(P)
    callback(result) // result does not implement interface. It's a pointer to a type, not a type
}

此处示例:https://go.dev/play/p/NVo4plR1R-O

您可以使用类型参数声明接口,使其要求实现它的类型是指向其类型参数的指针。

type A[P any] interface {
    SomeMethod()
    *P
}

因此,您还需要稍微修改处理程序的签名。

func Handler[P any, T A[P]](callback func(result T)) {
    result := new(P)
    callback(result)
}
Handler(func(a *Aimpl) { fmt.Printf("%#v\n", a) })

https://go.dev/play/p/PY5iE7WoHt3


如果您不能修改 A 的定义,那么,正如您已经发现的那样,您可以将其包装到您自己的定义中。

type MyA[P any] interface {
    A
    *P
}
func Handler[P any, T MyA[P]](callback func(result T)) {
    result := new(P)
    callback(result)
}
Handler(func(a *Aimpl) { fmt.Printf("%#v\n", a) })

https://go.dev/play/p/50uzqCShnKb

一般来说,当你把约束写成P any, T MyA[P]时,没有信息表明P实现了某个接口。事实上 P 字面意思是 anything 因为你用 any 限制了它。 T 然后用 P 实例化并生成有效的实现程序这一事实与 P 本身无关。对于编译器,没有迹象表明 *P(指向任何类型的指针)实现了 A.

如果您想在回调签名中保留 A 的同时完成这项工作,请使用转换。您仍然需要捕获基类型才能传递 non-nil 指针实现程序,因此您将接口与指针约束 *T 包装在一起的方法原则上是正确的。

以后使用未命名的约束接口。类型参数的名称也是倒置的——T 是基本类型,P 是指针类型(逻辑上...):

func (a *Aimpl) SomeMethod() { fmt.Printf("called, %T, %t\n", a, a == nil) }

func Handler[P interface { *T; A }, T any](callback func(A)) {
    p := P(new(T))
    callback(p)   
}

然后你需要用 *Aimpl:

显式实例化 Handler
func main() {
    Handler[*Aimpl](func(result A) { result.SomeMethod() }) 
    // prints: called, *main.Aimpl, false
}

游乐场:https://go.dev/play/p/96_UFVnfyO-

更改回调类型的另一个选项允许您使用类型推断而不是显式实例化,因为这样您就已经知道实现者了。但目前尚不清楚 A 如何适合图片:

func main() {
    // using the concrete type instead of A
    Handler(func(result *Aimpl) { result.SomeMethod() })
}