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

Time:05-03

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

CodePudding user response:

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

CodePudding user response:

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() })
}
  • Related