倭マン's BLOG

くだらない日々の日記書いてます。 たまにプログラミング関連の記事書いてます。 書いてます。

Go 言語の io パッケージに定義されているインターフェース型を一気見する

Go 言語のいろいろなパッケージを使ってみるシリーズ(目次)。 今回は io パッケージにたくさん定義されているインターフェース群をザッと見ていきます。 実装には立ち入りません。

io パッケージに定義されているインターフェースと定数

io パッケージに定義されているインターフェース型には以下のようなものがあります:

// バイト I/O
type ByteReader
type ByteScanner
type ByteWriter

// ルーン I/O
type RuneReader
type RuneScanner

// バイトスライス I/O
type Reader
type Writer
type Seeker
type Closer

// Seeker のための定数
const (
  SeekStart   = 0 // ファイルの先頭から
  SeekCurrent = 1 // ファイルの現在の読み書き位置から
  SeekEnd     = 2 // ファイルの末尾から
)

type ReadWriter
type ReadSeeker
type ReadCloser
type WriteSeeker
type WriteCloser

type ReadWriteCloser
type ReadWriteSeeker

// その他バイトスライス I/O
type ReaderAt
type ReaderFrom

type WriterAt
type WriterTo

以下でそれぞれの分類を見ていきましょう。

バイト I/O

バイト (byte) 1つずつ読み書きするために使うインターフェースには

  • io.ByteReader
  • io.ByteScanner
  • io.ByteWriter

の3つがあります。 それぞれの型の定義は簡単なので見ておきましょう:

type ByteReader interface{
  ReadByte() (byte, error)
}

type ByteScanner interface{
  ByteReader
  UnreadByte() error
}

type ByteWriter interface{
  WriteByte(c byte) error
}

クラス図っぽく書くとこんな感じ:

f:id:waman:20171007120623p:plain

上図の矢印は継承っぽく書いてますが、Go には継承がなくコンポジションとなります。 ただし、ここでは代入可能な方向くらいの意味です:

  var bs io.ByteScanner = strings.NewReader("Hello, world!")
  var br io.ByteReader = bs  // 代入可能

ルーン I/O

Go のルーン (rune) は単なる int32 ですね。 上記のバイトの代わりに、ルーンを1つずつ読み込むインターフェースがあります:

  • io.RuneReader
  • io.RuneScanner

ルーンを書き込む RuneWriter というのは定義されてないようです*1。 各インターフェースの定義は以下のようになっています:

type RuneReader interface{
  ReadRune() (r rune, size int, err error)
}

type RuneScanner interface{
  RuneReader
  UnreadRune() error
}

クラス図風に書くとこんな感じ:

f:id:waman:20171007124911p:plain

バイトスライス I/O

バイトやルーンを1つずつ読み書きするインターフェースはそれぞれ2~3個しかないので特にどうということはありませんが、バイトスライスの読み書きをするインターフェースは結構たくさんあります。 基本になるのは次の4つです:

type Reader interface{
  Read(p []byte) (n int, err error)
}

type Writer interface{
  Write(p []byte) (n int, err error)
}

type Seeker interface{
  Seek(offset int64, whence int) (int64, error)
}

type Closer interface{
  Close() error
}

io.Reader, io.Writer, io.Closer はいいでしょう。 io.Seeker は次に読み書きする位置を指定する Seek メソッドを持ち、ランダムアクセスできる入出力を表しています。 以前の「Go 言語の os パッケージにある File 型を使ってみる (2) : os.File のメソッド」で *os.File に定義されている Seek メソッドを使いました。

io.ReadWriter インターフェースなどは、これらの4つのインターフェースのいくつかを組み合わせて作られています:

type ReadWriter interface{
  Reader
  Writer
}

名前を見ればどのインターフェースを組み合わせているかは分かると思いますが、この種のインターフェースを図にして整理しておきましょう:

f:id:waman:20171007125900p:plain

矢印はやはり代入可能な方向です。 結構ゴチャゴチャしてますが、io.Seeker と io.Closer を同時に実装している型は定義されていないというくらいが注意点でしょうか(os.File など、どちらも実装している具象型は他パッケージにありますが)。

その他バイトスライス I/O

上記のインターフェースの他にも、以下のバイトスライスを読み書きするインターフェースがあります:

  • io.ReaderAt
  • io.ReaderFrom
  • io.WriterAt
  • io.WriterTo

それぞれに1つずつ実装すべきメソッドが定義されています:

type ReaderAt interface {
  ReadAt(p []byte, off int64) (n int, err error)
}

type ReaderFrom interface {
  ReadFrom(r Reader) (n int64, err error)
}

type WriterAt interface {
  WriteAt(p []byte, off int64) (n int, err error)
}

type WriterTo interface {
  WriteTo(w Writer) (n int64, err error)
}

ここでは各メソッドの使い方は見ませんが、io.ReaderAt と io.WriterAt は *os.File によって実装されているので「Go 言語の os パッケージにある File 型を使ってみる (2) : os.File のメソッド」で使ってみました。 まぁ、名前から動作が分かると思いますが、指定した位置から読み書きをするランダムアクセス用のメソッド(型)です。 io.ReaderFrom, io.WriterTo は他のパッケージで具象型が出てきたときにやる予定(たぶん bufio パッケージ)。

今回は io パッケージに定義されているインターフェース型を見てきましたが、io パッケージにはここで見たインターフェースを実装するいくつかの具象型があるので、次回はそれらを使ってみる予定。

【追記】

  • io.Seeker のための定数を追記しました。

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

*1:バイトやバイトスライス、文字列を書き込める bufio.Writer には WriteRune メソッドも定義されてるんですが。