Golang’s Reader Interface

Michael Schuett
2 min readJan 27, 2015

--

Although this is going to specifically cover implementing the io.Reader interface I think it’s a good overview of implementing interfaces in general. Most articles I have found on the matter are simple examples that don't have much real world value such as implementing an Animal interface.

We will start by looking at what we need to implement in order for our struct to satisfy the io.Reader interface. From the go docs this is shown.

type Reader interface {
Read(p []byte) (n int, err error)
}

This looks simple as all we have to do is implement a Read “method” on our struct of choice. For this example I will implement a struct called Stringer that will output a property of string when it’s read method is called. This looks as such.

type Stringer struct {
stringer string
read bool
}

Now we go about implementing the io.Reader interface which is just a Read function that accepts a slice of bytes and outputs an int and error.

func (s Stringer) Read(p []byte) (n int, err error) {}

You may be able to tell from looking at this if you have worked in go for a while that this will not work. That is because we have no way to keep track of what has been read before since we don't have a pointer to the underlying structure. This is an issue because calls such as ioutil.ReadAll call Read multiple times and expect to be notified via an error when it has reached the end of the file. If this is not done you will run out of memory.

What we actually want is a function that has a header like such.

func (s *Stringer) Read(p []byte) (n int, err error)

The issue is when you implement this method and try and pass it to something that accepts an io.Reader you will get the following message. Here is a link to the full code http://play.golang.org/p/E5JbDw8Vak.

Stringer does not implement io.Reader (Read method has pointer receiver)

To get around this you want to make a pointer to a structure and pass that in. This will throw another error but if Reader was implemented properly this would work http://play.golang.org/p/gyMcTp2ALX.

Now to actually implement a working Read method.

func (r *Reader) Read(p []byte) (n int, err error) {
if r.done {
return 0, io.EOF
}
for i, b := range []byte(r.read) {
p[i] = b
}
r.done = true
return len(r.read), nil
}

As you can see since we can manipulate the underlying struct we can flag when read should stop being called. A full implementation may look like this http://play.golang.org/p/ejpUVOx8jR.

Many thanks to the go-nuts IRC channel for putting up with me and being one of the post helpful programming IRCs I have encountered.

--

--