gopointersgenericsinterface

Can not convert a generic struct type to a generic interface type in golang


Given there's a generic interface:

type Packet interface {
    Content() int
}

type Recycler[T any] interface {
    Get() *T
}

and their implementation:

type packet struct {
    content int
}

type BaseRecycler[T any] struct {
    t T
}

its impossible to convert the implementation to the interface:

    r := &BaseRecycler[packet]{}
    r1 := r.(Recycler[Packet]) 
        fmt.Println(r3.Get().Content()) // compile error: Unresolved reference 'Content'

here is a live code example: https://go.dev/play/p/qbnYfyDd22A

The error is because the Packet interface align with the *packet instead of packet. Could anyone suggest how to handle this?


Solution

  • You are assuming covariance in type parameters, which Go does not have - types must match exactly. You would need variance annotations like Kotlins in and out or Javas wildcards for that.

    As the FAQ states:

    In Go method types must match exactly, [...]. Programmers who want covariant result types are often trying to express a type hierarchy through interfaces. In Go it’s more natural to have a clean separation between interface and implementation.


    To be a little more concrete, you could implement your packet like (Go Playground):

    type packet struct {
        content int
    }
    
    func (p packet) Content() int {
        return p.content
    }
    

    and then an interface abstracting that:

    type Packet interface {
        Content() int
    }
    

    Also the BaseRecycler:

    type BaseRecycler[T any] struct {
        t T
    }
    
    func (b BaseRecycler[T]) Get() T {
        return b.t
    }
    

    and an abstraction thereof:

    type Recycler[T any] interface {
        Get() T
    }
    

    Note some simplifications: The methods don't have pointer receivers (they serve no purpose in this example) and BaseRecycler does return a type, not an address.

    Now you can simply use that:

        var r Recycler[Packet] = BaseRecycler[Packet]{t: packet{}}
        fmt.Println(r.Get().Content())
    

    Also, you could define a PacketRecycler which has a concrete packet type and is not generic:

    type PacketRecycler struct {
        t packet
    }
    
    func (p PacketRecycler) Get() Packet {
        return p.t
    }
    

    and use it in an identical fashion:

        var r1 Recycler[Packet] = PacketRecycler{}
        fmt.Println(r1.Get().Content())