Reputation: 108
I'm trying to implement the interface Interface
using generics. It has one method which accepts another Interface as a parameter:
type SubInterface interface {
SendResponse(string)
}
type Interface interface {
Subscribe(SubInterface)
}
I've come up with the following generic version of those interfaces:
type GenericSubInterface[T any] interface {
SendResponse(T)
}
type GenericInterface[Res any] interface {
Subscribe(GenericSubInterface[Res])
}
I would expect GenericInterface[string]
to be assignable to Interface
but it somehow isn't.
var a Interface
var b GenericInterface[string]
// cannot use b (variable of type GenericInterface[string]) as Interface value in assignment: GenericInterface[string] does not implement Interface (wrong type for method Subscribe)
// have Subscribe(GenericSubInterface[string])
// want Subscribe(SubInterface)
a = b
Creating a generic implementation of Interface
doesn't work either:
type GenericImplementation[Res any] struct {
}
func (i *GenericImplementation[Res])Subscribe(ss GenericSubInterface[Res]) {
var msg Res
ss.SendResponse(msg)
}
// cannot use &GenericImplementation[string]{} (value of type *GenericImplementation[string]) as Interface value in variable declaration: *GenericImplementation[string] does not implement Interface (wrong type for method Subscribe)
// have Subscribe(GenericSubInterface[string])
// want Subscribe(SubInterface)
var c Interface = &GenericImplementation[string]{}
What seems weird to me is that the sub-interfaces are assignable to each other:
var d SubInterface
var e GenericSubInterface[string]
// works fine
d = e
The problem only seems to occur when interfaces are nested somehow. Is there any way around this that I can implement Interface
using generics for types other than string
?
Upvotes: 3
Views: 521
Reputation: 108
I've found a solution to satisfy Interface
using generics that fits my use case. Any nested interface has to be specified as an additional type parameter. This makes the instantiation a little more verbose but allows for GenericInterface
and GenericImplementation
to satisfy Interface
:
// Interface
type GenericInterface[Res any, Arg GenericSubInterface[Res]] interface {
Subscribe(Arg)
}
var a Interface
var b GenericInterface[string, SubInterface]
a = b // works
// Implementation
type GenericImplementation[Res any, Arg GenericSubInterface[Res]] struct {
}
func (i *GenericImplementation[Res, Arg]) Subscribe(ss Arg) {
var msg Res
ss.SendResponse(msg)
}
var c Interface = &GenericImplementation[string, SubInterface]{} // works
Upvotes: 1
Reputation: 44647
First, read Go interface: interface not implemented even though it is. The very first sentence of @colm.anseo's answer already summarizes the issue:
The signatures are not the same. The argument types are different
Then read How to implement generic interfaces?. Values of type SubInterface
are assignable to a particular instantiation of GenericSubInterface
, namely GenericSubInterface[string]
, because the methods end up being identical — SendResponse(string)
.
Interface
is not assignable to GenericInterface[string]
because the methods end up being not identical. The error message is as eloquent as it gets:
have Subscribe(GenericSubInterface[string])
want Subscribe(SubInterface)
You can use the generic version of your interfaces, however the methods must be implemented exactly. So you have to make the function AcceptInterface
generic too:
func AcceptInterface[T any](a GenericInterface[T]) {
}
func main() {
var b GenericInterface[string]
AcceptInterface(b)
}
Upvotes: 3