Fiduro
Fiduro

Reputation: 108

Generics: Implement nested interfaces

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?

Full playground example

Upvotes: 3

Views: 521

Answers (2)

Fiduro
Fiduro

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

blackgreen
blackgreen

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

Related Questions