I've just seen Go has incorporated generics in its latest release, and I'm trying to create a small project to understand how it works. I don't seem to figure out how it works apart from very simple functions being now generic. I'd like to be able to do things like this:
type Dao[RT any] interface {
FindOne(id string) *RT
}
type MyDao struct {
}
type ReturnType struct {
id int
}
func (m *MyDao) FindOne(id string) *ReturnType {
panic("implement me")
}
// how should this look like?
func NewMyDao() *Dao[ReturnType] {
return &MyDao[ReturnType]{}
}
Is that even possible? I don't seem to be implementing the interface that way, and I've tried many combinations of the same.
Is there a way to implement a generic interface? If not, is the alternative only to return the interface{}
type?
Best Answer
Types don't actually implement generic interfaces, they implement instantiations of generic interfaces. You can't use a generic type (including interfaces) without instantiation. From there, it is just like pre-generics Go, including the difference between methods with pointer receiver.
Therefore it is helpful to think what the methods that use type parameters would look like if you rewrote them with concrete types.
Let's consider a generic interface and some type:
There's a few possible cases
Interface with concrete type argument
Instantiate as
Getter[string]
, implemented by types with methodGet() string
Interface with type parameter as type argument
Functions that have type parameters may use those to instantiate generic types, e.g.
Getter[T]
. Implementors must have exactly theGet() T
method. For that to be valid, they are also generic and instantiated with the same type parameter:So this doesn't compile even if
T
isstring
Making
MyStruct
also parametrized works:Concrete interface with generic implementor
Let's reverse the previous cases. We keep the parametrized
MyStruct[T any]
but now the interface is not parametrized:In this case,
MyStruct
implementsGetter
only when it is instantiated with the necessary concrete type:Pointer receivers
This follows the same rules as above, but requires instantiating pointer types, as usual:
and it is the same if
MyStruct
is generic.So in your case, the mental exercise of replacing the type params with concrete types gives that
Dao[ReturnType]
has methodFindOne(id string) *ReturnType
. The type that implements this method is*MyDao
(pointer receiver), therefore: