3

I have a package in which I have two interfaces

package main

type A interface {
    Close()
}

type B interface {
    Connect() (A, error)
}

I have also two structures which implements these interfaces

type C struct {
}

func (c *C) Close() {

}

type D struct {
}

func (d *D) Connect() (*C, error) {
    c := new(C)
    return c, nil
}

Next I have a function which as a parameter wants an object which implements interface B

func test(b B) {
}

Finally, at the main() function I create D structure object and want to call test() function

func main() {
    d := new(D)
    test(d)
}

If I try to build that package I have an error.

cannot use d (type *D) as type B in argument to test: *D does not implement B (wrong type for Connect method) have Connect() (*C, error) want Connect() (A, error)

It is simple example of my code where I use external package and want to mock structures for tests. Is it any solution to use interfaces instead of types?

blackgreen
  • 34,072
  • 23
  • 111
  • 129
Abdulafaja
  • 262
  • 2
  • 8

2 Answers2

4

For implementing the interface there is one to concern about which is:

A Go type satisfies an interface by implementing the methods of that interface, nothing more. This property allows interfaces to be defined and used without having to modify existing code. It enables a kind of structural typing that promotes separation of concerns and improves code re-use, and makes it easier to build on patterns that emerge as the code develops.

The error you are getting because the struct D you are using as an argument to test function does not implement the interface. The reason behind this is that the function Connect you are using with receiver D is different. Since it has different return type:

func (d *D) Connect() (*C, error) { // the struct D does not implement the interface B because of wrong function definition to interface B function
    c := new(C)
    return c, nil
}

while if you want to implement the interface B the function definition along with its return type should match the function in interface B which is

type B interface {
    Connect() (A, error)
}

So if you want to implement the interface the Connect method you are using should match the Connect method of the interface B

package main

type A interface {
    Close()
}
type B interface {
    Connect() (A, error)
}

type C struct {
}

func (c *C) Close() {

}

type D struct {
}

func (d *D) Connect() (A, error) {
    c := new(C)
    return c, nil
}

func test(b B) {}

func main() {
    d := new(D)
    test(d)
}

Check on Go Playground

Consider this simple interface to represent an object that can compare itself with another value:

type Equaler interface {
    Equal(Equaler) bool
}

and this type, T:

type T int
func (t T) Equal(u T) bool { return t == u } // does not satisfy Equaler

The argument type of T.Equal is T, not literally the required type Equaler.

In Go, the type system does not promote the argument of Equal; that is the programmer's responsibility, as illustrated by the type T2, which does implement Equaler:

type T2 int
func (t T2) Equal(u Equaler) bool { return t == u.(T2) }  // satisfies Equaler
Himanshu
  • 12,071
  • 7
  • 46
  • 61
  • 1
    Ok, I thought if *C implements A interface it is possible to use that. Imagine that C and D are from external package and I can't change their functions. How to mock them? – Abdulafaja Aug 21 '18 at 09:10
  • 1
    @Abdulafaja One thing you can do is import the struct from another package. If you cannot change the function then create a new one which should implement B. – Himanshu Aug 21 '18 at 09:22
  • thanks, it was the most helpful. I have created another structure which override external struct and I have added correct function (like here https://stackoverflow.com/questions/28800672/how-to-add-new-methods-to-an-existing-type-in-go). But still I think that compiler should pass my code – Abdulafaja Aug 21 '18 at 09:56
2

The returned type for your Connect method should be A and not *C.

The way you defined the Connect method is that it should return an interface, not a specific type. You will still be able to return *C as it implements the A interface.

package main

type A interface {
    Close()
}

type B interface {
    Connect() (A, error)
}

type C struct {
}

func (c *C) Close() {
}

type D struct {
}

func (d *D) Connect() (A, error) {
    c := new(C)
    println("successfully created new C:", c)
    return c, nil
}

func test(b B) {
    b.Connect()
}

func main() {
    d := new(D)
    test(d)
}

Outputs

successfully created new C: 0xe28f0

Try it out yourself here

Ullaakut
  • 3,554
  • 2
  • 19
  • 34
  • 1
    Thanks, but what if struct C and D are not in my package and I want to mock them? I can't edit these functions – Abdulafaja Aug 21 '18 at 09:08
  • @Abdulafaja You do not want to mock functions. Thats what you might have been told to do is the right thing. It is not. – Volker Aug 21 '18 at 11:19