Understanding golang Reader/Writer

Original post at Medium - Understanding golang Reader/Writer

Reader/Writer are basic interfaces designed in Golang. For example, if a struct have a function like:

type Example struct {
}
func (e *Example) Write(p byte[]) (n int, err error) {
}
func (e *Example) Read(p byte[]) (n int, err error) {
}

We will say Example implements Read/Write interface, and if we want do a force type conversions, we will see Example is type of Reader&Writer

reader, err := example.(Reader)
if err != nil {
    // Shouldn't throw an error here.
}

But how Reader/Writer works ?

For example, File struct already implement the Read/Write function then call the syscall read/write function read the data from file.

func (f *File) Read(b []byte) (n int, err error) {
    if err := f.checkValid("read"); err != nil {
        return 0, err
    }
    n, e := f.read(b)
    if n == 0 && len(b) > 0 && e == nil {
        return 0, io.EOF
    }
    if e != nil {
        err = &PathError{"read", f.name, e}
    }
    return n, err
}
And another example here is
type Example struct {
    r File
}
func (e *Example) Read(p []byte)(n int, err error) {
    return e.f.Read(p)
}

This is an example see how do we pass the Reader/Writer from one struct to another, when example instance initialized with a File , example can read the data what f get and example still be a type of Reader.So we can build a Reader chain like

type A struct {
    b B
}
type B struct {
    c C
}
func (a *A) call() (r Reader, err error) {
    return a.b.c
}
// If A,B,C all implement Read function, then when we call 
// a.call function will get an C instance which is type of Reader.

So how to design the API for Reader/Writer struct ?

If we want build a chain of Reader/Writer and has a friendly API for others, how can we do ? In golang source code file we can see two type of API design for example:

func (a *A) call(in Writer) (out Writer, err error) {}
or
func (b *B) call(out Writer) (in Writer, err error) {}

First, there is no best practice for the API design, when you call the function you will be get two types of way,

// A
out, err := a.call(w)
io.Copy(w, reader)
// B
in, err := a.call(w)
io.Copy(in, reader)

hmm, it seems the exampleA is a very common way when we design the API, we apply parameter and then get the result. But has the pointer support just like C-lang, so we can have solution B. But B is used every where inside of golang, so when do we need use A. In my opinion, when we want write some describe data or meta data for content data, we need use A because inside of call function Writer/w is already initialized and we can access all the functions, props it has.

Conclusion

Reader/Writer are very important in golang, understanding how it works is very important and a friendly API is also a important things.

All this is my personal thoughts.

Refs:

  1. https://golang.org/pkg/io/#Copy
  2. https://github.com/xeodou/aesf