ruk·si

🐹 Go
io Package

Updated at 2014-02-06 01:46

This note is about Go built-in package io. It contains a few very improtant interfaces that must be understood to write good Go code.

io.Writer

Writer is an interface for writing bytes into an unspecified data stream.

type Writer interface {
    // Writes `len(p)` bytes from `p` into underlying data stream.
    // Returns the number of bytes written `n` and any error encountered `err`.
    // When `n < len(p)`, there must be an `err`.
    Write(p []byte) (n int, err error)
}

Examples of io.Writer usage.

package main

import (
    "fmt"
    "os"
    "bufio"
)

func rawWrite() {
    message := "LOL\n"
    bytesWritten, errWrite := os.Stdout.Write([]byte(message))
    if errWrite != nil {
        panic(errWrite)
    }
    fmt.Printf("%d bytes were written\n", bytesWritten)
}

func fmtWrite() {
    messageF := "Hello %s!\n"
    // fmt.Fprintf prints to a io.Writer interface.
    bytesWritten, errWrite := fmt.Fprintf(os.Stdout, messageF, "Tommi")
    if errWrite != nil {
        panic(errWrite)
    }
    fmt.Printf("%d bytes were written\n", bytesWritten)
}

func bufferedWrite() {
    totalBytesWritten := 0
    buffered := bufio.NewWriter(os.Stdout)
    fmt.Printf("%d bytes available in buffer\n", buffered.Available())
    bytesWritten, errWrite := buffered.WriteString("HELLO ")
    if errWrite != nil {
        panic(errWrite)
    }
    totalBytesWritten += bytesWritten;
    bytesWritten, errWrite = buffered.WriteString("WORLD")
    if errWrite != nil {
        panic(errWrite)
    }
    totalBytesWritten += bytesWritten;
    bytesWritten, errWrite =buffered.WriteString("!\n")
    if errWrite != nil {
        panic(errWrite)
    }
    totalBytesWritten += bytesWritten;
    fmt.Printf("%d bytes available in buffer\n", buffered.Available())
    errFlush := buffered.Flush() // Write buffered data to the io.Writer.
    if errFlush != nil {
        panic(errFlush)
    }
    fmt.Printf("%d bytes were written\n", totalBytesWritten)
    fmt.Printf("%d bytes available in buffer\n", buffered.Available())
}

func main() {
    rawWrite()
    fmtWrite()
    bufferedWrite()
}

io.Reader

Reader is an interface for reading bytes from an unspecified data stream.

type Reader interface {
    // Reads up to `len(p)` bytes into `p`, from underlying data stream.
    // Returns the number of bytes read `n` and any error encountered `err`.
    // n: (0 <= n <= len(p))
    Read(p []byte) (n int, err error)
}

io.Reader.Read callers should always process p when n > 0 before considering the error err. The error may just be normal EOF.

io.EOF // Normal end of input
io.ErrUnexpectedEOF // EOF in the middle of a fixed-size block or data structure
io.ErrNoProgress // Many calls to Read have failed to return any data or error

Custom implementations of io.Reader.Read should avoid returning n = 0 and err = nil as it can be treated as not progressing.

Next snippet will wait for command line input. When command line input is received, it will print the input in 16 byte chunks until there is nothing more to read.

package main

import (
    "fmt"
    "io"
    "os"
)

func WaitAndReadAndPrintChunk() {
    // Message can be 16 bytes long. Rest is left in the Reader.
    messageBytes := make([]byte, 16)
    bytesRead, errRead := os.Stdin.Read(messageBytes)
    if (bytesRead > 0) {
        // Processing of message must be done before erro checking so
        // no data is lost on reading.
        message := string(messageBytes[:bytesRead])
        fmt.Println(message)
    }
    if errRead == io.EOF {
        return // End-of-file, the program was terminated.
    } else if errRead != nil {
        panic(errRead)
    }
}

func main() {
    for { // Infinite loop.
        WaitAndReadAndPrintChunk()
    }
}

go run main
> 123456789 123456789 123456789 123456789 123456789 123456789
123456789 123456
789 123456789 12
3456789 12345678
9 123456789
> 123456789
123456789
> HELLO WORLD AND ALL THE AMAZING CREATURES!
HELLO WORLD AND
ALL THE AMAZING
CREATURES!

Sources