gonetwork-programminggo-interface

Go interfaces: is not net.Conn a io.ReadWriteCloser?


I have a question about the uses of interfaces in go. I am still learning the language, so please forgive me if it is a stupid question.

I am playing with a little application that accept and handle TCP connections. Because I want to test the business logic and not the network magic, I am trying to keep it a little bit abstract using interfaces (so I can simulate net.Conn object using simple io.ReadWriteCloser objects).

Here is my problem:

type Accepter interface {
    Accept() (io.ReadWriteCloser, error)
}

func NewTCPAccepter(address string) (Accepter, error) {
    accepter, err := net.Listen("tcp", address)
    if err != nil {
        return nil, err
    }
    return accepter, err
}

The error I see is:

net.Listener does not implement Accepter (wrong type for Accept method)
                have Accept() (net.Conn, error)
                want Accept() (io.ReadWriteCloser, error)

What I do not understand is why this is happening, because checking the documentation and the net lib source code, I see that net.Conn is actually implementing io.ReadWriteCloser.

What is my mistake? Thanks a lot!


Solution

  • I don't believe that stupid questions exist. If there is an honest question, there is an urge for knowledge, and it cannot be stupid by definition :)

    Go relies on method signature to decide if two interfaces are the same. You cannot say that two method signatures are equal if they return essentially different types.

    P.S. If I express my opinion, I don't think your Accepter abstraction is sound. I am struggling to explain what it does. Coming up with a good abstraction is hard, and I am pretty sure that the existing abstractions are already enough to do what you want -- test your business logic.

    Let's say your business logic parses a JSON document and converts it to a human-readable text. You can pack it to a function with the following signature:

    func DescribeDocument(src io.Reader, dst io.Writer) error {
        ...
    }
    

    You can pass an object that implements the net.Conn interface to this function in a production code or the bytes.Buffer object in a test code. No need to invent unnecessary abstractions :)