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

huangapple go评论83阅读模式
英文:

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)) {
  // 我想做的是 result := &Aimpl{} (或者是 T 的任何类型)
  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

英文:

I have an interface like this

type A interface {
  SomeMethod()
}

I have an implementation of this interface via a struct pointer:

type Aimpl struct {}

func (a *Aimpl) SomeMethod() {}

I have a generic function that takes in a function with an A parameter like so:

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

Also I should add that I cannot modify the definition of A (it's coming from a library). I've tried this:

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

But this code gives me an error:

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
}

Example here: https://go.dev/play/p/NVo4plR1R-O

答案1

得分: 6

你可以使用类型参数声明接口,以确保实现它的类型是其类型参数的指针。

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) })

如果你不能修改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) })
英文:

You can declare the interface with a type parameter to make it require that the type implementing it is a pointer to its type parameter.

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

With that you'll also need to modifier the handler's signature a bit.

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


If you can't modify the definition of A then, as you've already found out, you can wrap it into your own.

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

答案2

得分: 1

通常情况下,当你将约束写为P any, T MyA[P]时,没有信息表明P实现了某个特定的接口。实际上,P可以是任何东西,因为你用any对其进行了约束。T实例化为P并生成一个有效的实现者,与P本身无关。对于编译器来说,没有迹象表明*P(任意类型的指针)实现了A

如果你想在保持回调签名中的A的同时使其工作,可以使用转换。你仍然需要捕获基本类型,以便传递一个非空指针实现者,所以你将接口与指针约束*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() }) 
    // 输出:called, *main.Aimpl, false
}

Playground: https://go.dev/play/p/96_UFVnfyO-

更改回调的类型的另一个选项允许你使用类型推断而不是显式实例化,因为此时你已经知道实现者。但此时不清楚A如何适应这个情况:

func main() {
    // 使用具体类型而不是A
	Handler(func(result *Aimpl) { result.SomeMethod() })
}
英文:

In general, when you write the constraints as P any, T MyA[P] there is no information that P implements a certain interface. In fact P is literally anything since you constrained it with any. The fact that T is then instantiated with P and produces a valid implementor, is unrelated to P itself. For the compiler there is no indication that *P (a pointer to any type) implements A.

If you want to make this work while keeping A in the callback signature, use a conversion. You still need to capture the base type in order to pass a non-nil pointer implementor, so your approach to wrap the interface together with a pointer constraint *T is correct in principle.

Hereafter using an unnamed constraint interface. Names of the type params are also inverted — T is the base type and P is the pointer type (logically...):

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)   
}

Then you need to instantiate Handler explicitly with *Aimpl:

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

Playground: https://go.dev/play/p/96_UFVnfyO-

The other option to change the type of the callback allows you to use type inference instead of explicit instantiation, because then you already know the implementor. But at this point it's unclear how A fits in the picture:

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

huangapple
  • 本文由 发表于 2022年5月3日 01:41:40
  • 转载请务必保留本文链接:https://go.coder-hub.com/72090387.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定