Go接口方法返回接口与方法返回具体类型不匹配

I'm trying to get Go interfaces to work, and the problem I am having is that a Go interface method returning a go interface only works if the implementation declares the interface and not a concrete type that implements the interface.

In the following example I receive:

prog.go:29: cannot use &f (type *Bar) as type StringerGetter in argument to Printer: *Bar does not implement StringerGetter (wrong type for GetStringer method) have GetStringer() *Foo want GetStringer() fmt.Stringer

package main

import "fmt"

type Foo struct {
    val string
}

func (f *Foo) String() string {
    return f.val
}

type StringerGetter interface {
    GetStringer() fmt.Stringer
}

type Bar struct{}

func (b *Bar) GetStringer() *Foo {
    return &Foo{"foo"}
}

func Printer(s StringerGetter) {
    fmt.Println(s.GetStringer())
}

func main() {
    f := Bar{}
    Printer(&f)
}

However if I change func (b *Bar) GetStringer() *Foo { to func (b *Bar) GetStringer() fmt.Stringer { it works as expected.

Foo does fulfill fmt.Stringer

The rub is that the above is simple example code illustrating the problem.

In the case of the code I am trying to write, the interfaces I created are in my "main" package, whereas the concrete types I'm writing interfaces against are in a 3rd party package I'd prefer not to modify.

I want to be able to write my own structs as well that fulfill said interfaces, so interfacing against the concrete return type isn't an option either.

I have to assume there is a way to do this.

I want to be able to write my own structs as well that fulfill said interfaces, so interfacing against the concrete return type isn't an option either.

"I want" and Go does not match well.

I have to assume there is a way to do this.

No, there is not. Interfaces match exactly. Go has no notion of supertype or specialisation.

As others have stated, Go does not support what you are trying to do.

Have you considered that you may be over complicating your design a bit?

You don't often find factory of factory patterns in Go like you would in other languages like Java (for example).

At least, that's what I assume you are doing by having interfaces that return interfaces that return a thing.

You could always make a new package to hold the factory factory interfaces.

But usually, I'd first try to see if that level of abstraction is worth the readability hit you will take in the code base. There may be a simpler way to solve your problem.

Your problem is that you're trying to cause someone else's (i.e. another package's) type to conform to an interface that it doesn't actually conform to. You can't do that. But you can make your type conform to anything you want. And you can make your type wrap their type. There are two ways to do this. Here's one:

type MyBar Bar

func (b *MyBar) GetStringer() fmt.Stringer {
    return &Foo{"foo"}
}

func main() {
    f := MyBar(Bar{})
    Printer(&f)
}

Now we've created a new type, MyBar that wraps a Bar and provides the interface you want. This approach doesn't "inherit" any other functionality from Bar, however. You have to wrap every individual thing you want, and you may have to reimplement (because you don't have access to the underlying impl).

But you can also embed Bar, and "inherit" everything you don't change and get access to the underlying impl (which is probably closer to what you want here). For example:

type MyBar struct{ Bar }

func (b *MyBar) GetStringer() fmt.Stringer {
    return b.Bar.GetStringer()
}

func main() {
    f := MyBar{Bar{}}
    Printer(&f)
}

Now if Bar has additional methods, they'll show up on MyBar for free. Either can be useful depending on your problem. You can use this technique to help other packages types conform to whatever you want.

This doesn't mean that Bar and MyBar are interchangeable, but they're pretty close and it's pretty easy to convert between them as you need.