Go语言中的io.Reader和io.Writer以及它们的实现

在使用Go语言的过程中,无论你是实现web应用程序,还是控制台输入输出,又或者是网络操作,不可避免的会遇到IO操作,使用到io.Reader和io.Writer接口。也也许对这两个接口和相关的一些接口很熟悉了,但是你脑海里确很难形成一个对io接口的继承关系整天的概貌,原因在于godoc缺省并没有像javadoc一样显示官方库继承关系,这导致了我们对io接口的继承关系记忆不深,在使用的时候还经常需要翻文档加深记忆。本文试图梳理清楚Go io接口的继承关系,提供一个io接口的全貌。

io接口回顾

首先我们回顾一下几个常用的io接口。标准库的实现是将功能细分,每个最小粒度的功能定义成一个接口,然后接口可以组成成更多功能的接口。

最小粒度的接口

1
2
3
type Reader interface {
Read(p []byte) (n int, err error)
}
1
2
3
type Writer interface {
Write(p []byte) (n int, err error)
}
1
2
3
type Closer interface {
Close() error
}
1
2
3
type Seeker interface {
Seek(offset int64, whence int) (int64, error)
}
1
2
3
type ReaderFrom interface {
ReadFrom(r Reader) (n int64, err error)
}
1
2
3
type WriterTo interface {
WriteTo(w Writer) (n int64, err error)
}
1
2
3
type ReaderAt interface {
ReadAt(p []byte, off int64) (n int, err error)
}
1
2
3
type WriterAt interface {
WriteAt(p []byte, off int64) (n int, err error)
}

以及

1
2
3
type ByteReader interface {
ReadByte() (byte, error)
}
1
2
3
type ByteWriter interface {
WriteByte(c byte) error
}

ByteScanner比ByteReader多了一个UnreadByte方法。

1
2
3
4
type ByteScanner interface {
ByteReader
UnreadByte() error
}
1
2
3
type RuneReader interface {
ReadRune() (r rune, size int, err error)
}
1
2
3
4
type RuneScanner interface {
RuneReader
UnreadRune() error
}

组合接口

Go标准库还定义了一些由上面的单一职能的接口组合而成的接口。

1
2
3
4
type ReadCloser interface {
Reader
Closer
}
1
2
3
4
type ReadSeeker interface {
Reader
Seeker
}
1
2
3
4
type ReadWriter interface {
Reader
Writer
}
1
2
3
4
5
type ReadWriteCloser interface {
Reader
Writer
Closer
}
1
2
3
4
5
type ReadWriteSeeker interface {
Reader
Writer
Seeker
}
1
2
3
4
type WriteCloser interface {
Writer
Closer
}
1
2
3
4
type WriteSeeker interface {
Writer
Seeker
}

从它们的定义上可以看出,它们是最小粒度的组合。

最小接口的扩展

有些结构体struct实现并且扩展了接口,这些结构体是。

1
2
3
4
type LimitedReader struct {
R Reader // underlying reader
N int64 // max bytes remaining
}
1
2
3
type PipeReader struct {
// contains filtered or unexported fields
}
1
2
3
type PipeWriter struct {
// contains filtered or unexported fields
}
1
2
3
type SectionReader struct {
// contains filtered or unexported fields
}

下面我会将它们的继承关系画出来。

一些辅助方法

一些辅助方法可以生成特殊类型的Reader或者Writer:

1
2
3
4
5
func LimitReader(r Reader, n int64) Reader
func MultiReader(readers ...Reader) Reader
func TeeReader(r Reader, w Writer) Reader
func MultiWriter(writers ...Writer) Writer

继承关系

当然,Go语言中并没有Java中那样的继承关系,而是基于duck type形式实现,我用下图尝试展示Go io接口的继承关系。

其中黄色是 bufio 包下的类型,
绿色是 archive.tar 包下的类型,
蓝色是 bytes 包下的类型,
粉红色是 strings包下的类型,
紫色是 crypto.tls 包下的类型。

Randmath.rand包下的类型。
Fileos包下的内容。
`Rand`左边的那个Readerimage.jpeg下的内容。

我们最常用的是包iobytesbufio下的类型,所以这几个包下的类型要记牢,在第三库中经常会出现它们的身影。

上图中并没有把mime/multipart.Filenet/http.File列出来,主要是图太复杂了,它们实现的接口和os.File类似。

当然你可能会问,你怎么整理的它们的继承关系?事实上,你可以通过godoc -analysis=type -http=:6060生成带继承关系的Go doc,并且它还可以将你本地下载的库中的继承关系也显示出来。

参考文档

  1. https://golang.org/pkg/io/
  2. https://golang.org/pkg/bufio/
  3. https://golang.org/pkg/bytes/
  4. https://medium.com/@benbjohnson/go-walkthrough-io-package-8ac5e95a9fbd#.er7vmwvb3
  5. https://medium.com/@blumenmann/a-simple-beginners-tutorial-to-io-writer-in-golang-2a13bfefea02#.sm0gq7rn0
  6. https://www.reddit.com/r/golang/comments/4z2eo2/noob_question_how_to_discover_interface/