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?

1
  • 1
    Change Connect() to return (A, error). Commented Aug 21, 2018 at 8:54

2 Answers 2

5

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
Sign up to request clarification or add additional context in comments.

3 Comments

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 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.
thanks, it was the most helpful. I have created another structure which override external struct and I have added correct function (like here stackoverflow.com/questions/28800672/…). But still I think that compiler should pass my code
3

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

2 Comments

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 You do not want to mock functions. Thats what you might have been told to do is the right thing. It is not.

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.