はじめに
Goのioパッケージにはio.Readerやio.Writerのインターフェイスを有効活用するための便利なメソッドがいくつか用意されています。
自分はその中の一つであるio.TeeReader
の挙動を理解するのに手こずったので、備忘録として挙動や使い方を残しておこうと思います。
io.TeeReaderとは
io.TeeReaderメソッドは、引数で渡したio.Readerを、読み込まれた内容を2番目の引数で渡したio.Writerに書き込むio.Readerに変換するメソッドです。
自分はio.Readerを進化させるメソッドだと理解しました。イメージはこんな感じです。
サンプルコード
package main
import (
"fmt"
"io"
"os"
"strings"
)
func main() {
var r io.Reader = strings.NewReader("some io.Reader stream to be read\n")
var w io.Writer = os.Stdout
teeReader := io.TeeReader(r, w)
buf := make([]byte, 1024)
_, _ = teeReader.Read(buf)
fmt.Println(string(buf))
}
実行結果
$ go run main.go
some io.Reader stream to be read
some io.Reader stream to be read // こちらがfmt.Printlnによる出力
上記の挙動を図にすると以下のようになります。
ユースケース
ユースケースとしては、クライアントからの入力を読み込むと同時にログに出力する、といったものがあるらしいです(2)。
自分は1つの画像バイナリを複数の形式にデコードしたいときに使用しました。
おわりに
ちなみにio.TeeReaderの語源はUnixコマンドのteeらしいです(2)。たしかに、↑の図を見るとデータの流れがT字になっています。
記事を書いていて気がついたのですが、自分がTeeReaderの挙動をいまいち理解できなかったのは、そもそもio.Readerのイメージが正確では無かったからだと気が付きました。io.Readerは『Readメソッドを実装する構造体』という意味なので、「読まれる」側、つまり受動者です。
しかし、自分はio.Readerを「読む」側だと無意識のうちにイメージしてしまっていたので、理解に躓いたのだと思いました。