Writer
Writer is the interface that wraps the basic Write method. io package makes
extensive use of this interface. Writer is implemented by os.File. Which
in the io package is used to reference to Stdin, StdOut, and StdErr:
var (
Stdin = NewFile(uintptr(syscall.Stdin), "/dev/stdin")
Stdout = NewFile(uintptr(syscall.Stdout), "/dev/stdout")
Stderr = NewFile(uintptr(syscall.Stderr), "/dev/stderr")
)
How does writer works?
Write writes len(p) bytes from p to the underlying data stream. It returns the number of bytes written from p (0 <= n <= len(p)) and any error encountered that caused
writefunction to stop early. Write must return a non-nil error if it returns n < len(p).Writemust not modify the slice data, even temporarily.
Implementations must not retain p.
type Writer interface {
Write(p []byte) (n int, err error)
}
To see an implementation, we can check the one for os.File. There
are some OS dependent helper functions that help Write doing their
job. But essentailly it is a transparent copy of its description.
Check a fragment of the POSIX implementatiof of Write from the source
code in the os package:
// Write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
// Write returns a non-nil error when n != len(b).
func (f *File) Write(b []byte) (n int, err error) {
// for posix f.checkValid returns nil unless f is nil
if err := f.checkValid("write"); err != nil {
return 0, err
}
n, e := f.write(b)
if n < 0 {
n = 0
}
if n != len(b) {
err = io.ErrShortWrite
}
epipecheck(f, e)
if e != nil {
err = f.wrapErr("write", e)
}
return n, err
}
Side note: it's important to see that write will block the thread. There's
an ongoing discussion to provide
an async API for files, but it's still being considered.
Reader
Similarly, Reader is the reading counterpart. It is also an interface that wraps
the Read method.
Readreads up to len(p) bytes into p. It returns the number of bytes read (0 <= n <= len(p)) and any error encountered. Even ifReadreturns n < len(p), it may use all of p as scratch space during the call. If some data is available but not len(p) bytes,Readconventionally returns what is available instead of waiting for more.
It is defined as:
type Reader interface {
Read(p []byte) (n int, err error)
}
As an implementor of Read, when there is nothing else to read, the Standard
Library suggest to return 0, EOF instead. 0, nil is only acceptable when
p length is equal to zero. But that's not happening very often.
Additionally, the Standard Library suggest first reading the n bytes, and then
handling the error if there was any. It adds that:
Doing so correctly handles I/O errors that happen after reading some bytes and also both of the allowed EOF behaviors.
Read implementation for type os.File relies on a helper function read that
does all the work (except error handling). read itself relies on poll.FD (on *nix),
which is in charge of checking whether there is anything to be read on that
file descriptor. For the very curious reader, go and check its implemenation of
FD's Read here.
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)
return n, f.wrapErr("read", e)
}