deitch
deitch

Reputation: 14581

How do I have a golang interface implementation return implementation of another interface?

Specific example is here: https://play.golang.org/p/ZNmviKWLwe

I have an interface A. I expect all implementations of A to have a certain signature (hence the interface). I expect one of those to return something useful that implements another interface.

type Box interface {
    GetToy(int) (*Toy, error)
}
type Toy interface{
    Play()
}


type CarBox struct {
   Length int
   Width int
   Height int
   toys []*Matchbox
}
type Matchbox struct {}

func (b *CarBox) Size () int {
    return b.Length*b.Width*b.Height
}
func (b *CarBox) GetToy(j int) (*Matchbox, error) {
    return b.toys[j], nil
}
func (m *Matchbox) Play() {
    fmt.Println("Zoom!")
}

But of course, it doesn't work, because GetToy() returns *Matchbox and not *Toy, even though Matchbox is a perfectly acceptable implementation of Toy. And, no, it isn't because of the pointers; it gives the same result whether I return *Toy in GetToy or just Toy:

tmp/sandbox721971102/main.go:33:15: cannot use CarBox literal (type CarBox) as type Box in return argument:
    CarBox does not implement Box (wrong type for GetToy method)
        have GetToy(int) (*Matchbox, error)
        want GetToy(int) (*Toy, error)

Obviously my pattern doesn't match how go works. What is the "correct" way to go about doing this?

Upvotes: 3

Views: 7061

Answers (2)

Eugene Lisitsky
Eugene Lisitsky

Reputation: 12845

Some fixes: https://play.golang.org/p/-mDr5SDZvV

Pointer to interface is extremely rearly required thing. Use interface - it can contain a concrete type.

UPDATE: If you wish to have a method with pointer receiver like func (b *CarBox) GetToy(j int) you may return pointer from Tester function (pay attention to &):

func Tester() Box {
    return &CarBox{}
}

This way interface value returned from Tester will conclude a pointer to the CarBox instance. Thus Go runtime will be able to run methods with pointer receiver. Otherwise there would be only a value not a pointer. So only methods with value receivers could be called.

Upvotes: 1

abhink
abhink

Reputation: 9116

First of all you almost never need a pointer to an interface type. So GetToy method signature should be changed to:

type Box interface {
    GetToy(int) (Toy, error) // use Toy, not *Toy
}

Now, note that interfaces are satisfied by types implicitly. As a result, by implementing the Play() method, *Matchbox implicitly satisfies the Toy interface (note the *, the type implementing the Toy interface is pointer to Matchbox and not Matchbox).

This means a value of type *Matchbox can be substituted for type Toy. So what remains is to fix the GetToy method defined on *CarBox:

func (b *CarBox) GetToy(j int) (Toy, error) {
    return b.toys[j], nil
}

Now GetToy above will return a *Matchbox which does implement the Toy interface.

Upvotes: 6

Related Questions