Single method interfaces end up being extremely powerful — more so than even the language creators thought. Let’s have a look at io.Reader:
Pretty simple; a Reader is any type that implements the
For those unfamiliar with these interfaces; you pass in a slice of bytes, and Read is asked to fill it with its data — which is does until it runs out of data. It returns the number of bytes read (in
n) or an error if something goes wrong. Additionally, if it has finished reading, it will return a special marker error called
io.EOF (end of file).
Examples of readers
There are many kinds of Reader types available in the standard library, and you’ve almost certainly used at least one of them.
If you open a file for reading, the object returned is an
os.File, which is a Reader (it implements the Read method):
You can also make a Reader from a normal string using
The body data from an
http.Request is a Reader:
A bytes.Buffer is a Reader:
There are many more throughout the standard library — and in most third-party packages too, since it’s good practice to use them wherever you can.
Now we have a few Reader types — let’s explore ways in which they can be used. You can read from them directly (this turns out to be the least useful use case):
ioutil.ReadAll lets you read everything from a Reader, and get the raw byte data:
io.Copy lets you read ALL bytes from an io.Reader, and write it to an io.Writer:
The JSON decoder lets you decode directly from a Reader:
If you’re reading bytes that have been gzipped, you can wrap the io.Reader in a gzip.Reader:
Now reading from the new reader will decompress as you read.
Take io.Reader when you can
If you’re designing a package or utility (even if it’s an internal thing that nobody will ever see) rather than taking in strings or byte slices, consider taking in an io.Reader if you can for data sources. Because suddenly, your code will work with every type that implements io.Reader. So this:
Then if someone wants to use it with a string, they can: