Struct does not implement interface if it has a method whose parameter implements interface

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?


Solution 1:

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

Solution 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