8

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.

2
  • 2
    Interfaces are generally explicit contracts in any language. The main reason here that you can't have your method signature return Foo is because the interface says it returns a fmt.Stringer. This is not isolated to Go. For example, this C# code also fails because of the same reason: dotnetfiddle.net/4xZ2tk. The compiler simply cannot guarantee that you're going to return an object that satisfies the interface unless you tell it that you promise you will (by having the signature match the interface). Commented Dec 9, 2015 at 22:48
  • 1
    The difference though is C# interfaces are checked on definition, Go interfaces are checked on use. It would seem like the compiler would be able to check that return interface statically. Commented Dec 10, 2015 at 1:03

3 Answers 3

6

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.

Sign up to request clarification or add additional context in comments.

2 Comments

"Your problem is that you're trying to cause someone else's type to conform to an interface that it doesn't actually conform to. You can't do that." Isn't that specifically what Go's type system is designed to let you do. Isn't that why they're checked on use rather than definition?
@donatJ not across packages. Packages are masters of their own types. What if two different packages extended the same type in different ways? Which would win? You can have complex rules, but Go just says "you can't do that."
3

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.

Comments

1

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.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.