[インデックス 11679] ファイルの概要
このコミットは、Go言語の標準ライブラリ io/ioutil
パッケージ内の ReadFile
および ReadAll
関数のドキュメントを更新し、EOF
(End-of-File) の振る舞いについて明確化するものです。具体的には、これらの関数がファイルの終端に達した場合でも、成功時には nil
エラーを返すという仕様を明記しています。これにより、ユーザーが EOF
をエラーとして誤って処理することを防ぎ、より堅牢なコードを書くための指針を提供します。
コミット
commit 2f8e5a5f88b0d744fe0c7c13b53e363d38124d88
Author: Rob Pike <r@golang.org>
Date: Wed Feb 8 11:40:56 2012 +1100
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/2f8e5a5f88b0d744fe0c7c13b53e363d38124d88
元コミット内容
io/ioutil: document EOF behavior in ReadFile and ReadAll
Fixes #2862.
R=golang-dev, n13m3y3r, iant
CC=golang-dev
https://golang.org/cl/5646048
変更の背景
Go言語の io
パッケージにおける Reader
インターフェースは、データの読み込み操作を抽象化しています。Read
メソッドは、読み込むデータがない場合に io.EOF
エラーを返します。しかし、io/ioutil
パッケージの ReadFile
や ReadAll
のような関数は、その名前が示す通り、ファイル全体またはリーダーから利用可能なすべてのデータを読み込むことを目的としています。
このような関数が EOF
に到達した際に io.EOF
をエラーとして返してしまうと、呼び出し元はそれが「ファイルの終端に達した」という正常な状態なのか、「読み込み中に予期せぬエラーが発生した」という異常な状態なのかを区別するのが難しくなります。特に、ReadFile
や ReadAll
は「すべてを読み込む」というタスクを完了した時点で成功とみなされるべきであり、その過程で EOF
に到達することは予期された振る舞いです。
このコミットは、このような混乱を避けるために、ReadFile
と ReadAll
が成功時には nil
エラーを返すことを明示的にドキュメントに追加することで、ユーザーがこれらの関数の EOF
処理について正しく理解できるようにすることを目的としています。これにより、io.EOF
をエラーとして扱うべきではないというGoの慣習に沿ったコードの記述が促進されます。
前提知識の解説
io.Reader
インターフェース
Go言語の io
パッケージは、I/Oプリミティブを提供します。その中でも io.Reader
インターフェースは、バイトストリームからの読み込み操作を抽象化する最も基本的なインターフェースの一つです。
type Reader interface {
Read(p []byte) (n int, err error)
}
Read
メソッドは、p
に最大 len(p)
バイトを読み込み、読み込んだバイト数 n
とエラー err
を返します。
io.EOF
Read
メソッドが io.EOF
を返すのは、それ以上読み込むデータがないことを示す場合です。これはエラーというよりは、ストリームの終端に到達したことを示すシグナルとして扱われます。Goの慣習では、Read
が n > 0
と err == io.EOF
を同時に返すことは許容されており、これは「読み込めるデータはすべて読み込んだが、もうこれ以上データはない」という状態を示します。
エラーハンドリングの慣習
Go言語では、エラーは関数の最後の戻り値として返されるのが一般的です。慣習として、nil
はエラーがないことを意味し、非nil
の値はエラーが発生したことを意味します。io.EOF
は特殊なエラー値であり、通常はストリームの終端を示すために使用され、一般的なエラーとは区別して扱われるべきです。
技術的詳細
io/ioutil
パッケージの ReadFile
と ReadAll
関数は、それぞれファイル全体と io.Reader
からの全データを読み込むことを目的としています。これらの関数は、内部的に io.Reader
の Read
メソッドを繰り返し呼び出してデータを読み込みます。
Read
メソッドが io.EOF
を返した場合、それは「これ以上データがない」ということを意味し、ReadFile
や ReadAll
のような「すべてを読み込む」関数にとっては、そのタスクが正常に完了したことを示します。したがって、これらの関数が io.EOF
をエラーとして呼び出し元に伝播させるのは不適切です。なぜなら、それは読み込みが成功した結果であり、予期せぬ問題が発生したわけではないからです。
このコミットで追加されたドキュメントは、この重要な振る舞いを明確にしています。
-
ReadAll
のドキュメントに追加された説明:A successful call returns err == nil, not err == EOF. Because ReadAll is defined to read from src until EOF, it does not treat an EOF from Read as an error to be reported. (成功した呼び出しは
err == nil
を返し、err == EOF
ではありません。ReadAll
はソースからEOF
まで読み込むように定義されているため、Read
からのEOF
を報告すべきエラーとして扱いません。) -
ReadFile
のドキュメントに追加された説明:A successful call returns err == nil, not err == EOF. Because ReadFile reads the whole file, it does not treat an EOF from Read as an error to be reported. (成功した呼び出しは
err == nil
を返し、err == EOF
ではありません。ReadFile
はファイル全体を読み込むため、Read
からのEOF
を報告すべきエラーとして扱いません。)
これらの説明は、ReadFile
と ReadAll
が io.EOF
を内部的に処理し、読み込みが完了した場合には nil
エラーを返すというGoの設計思想と慣習を強調しています。これにより、開発者はこれらの関数からの戻り値をより正確に解釈し、io.EOF
を適切に処理することができます。
コアとなるコードの変更箇所
--- a/src/pkg/io/ioutil/ioutil.go
+++ b/src/pkg/io/ioutil/ioutil.go
@@ -34,11 +34,17 @@ func readAll(r io.Reader, capacity int64) (b []byte, err error) {
}
// ReadAll reads from r until an error or EOF and returns the data it read.
+// A successful call returns err == nil, not err == EOF. Because ReadAll is
+// defined to read from src until EOF, it does not treat an EOF from Read
+// as an error to be reported.
func ReadAll(r io.Reader) ([]byte, error) {
return readAll(r, bytes.MinRead)
}
// ReadFile reads the file named by filename and returns the contents.
+// A successful call returns err == nil, not err == EOF. Because ReadFile
+// reads the whole file, it does not treat an EOF from Read as an error
+// to be reported.
func ReadFile(filename string) ([]byte, error) {
f, err := os.Open(filename)
if err != nil {
コアとなるコードの解説
このコミットは、src/pkg/io/ioutil/ioutil.go
ファイルにドキュメントコメントを追加するものです。実際の関数のロジックには変更はありません。
-
ReadAll
関数の定義の上に、以下のコメントが追加されました。// A successful call returns err == nil, not err == EOF. Because ReadAll is // defined to read from src until EOF, it does not treat an EOF from Read // as an error to be reported.
このコメントは、
ReadAll
がリーダーからEOF
に到達するまで読み込むことを目的としているため、Read
メソッドがEOF
を返しても、それは成功を示すものであり、エラーとして報告すべきではないことを明確にしています。 -
ReadFile
関数の定義の上に、以下のコメントが追加されました。// A successful call returns err == nil, not err == EOF. Because ReadFile // reads the whole file, it does not treat an EOF from Read as an error // to be reported.
同様に、
ReadFile
もファイル全体を読み込むことを目的としているため、ファイルの終端に達してRead
がEOF
を返しても、それは成功を示すものであり、エラーとして報告すべきではないことを明記しています。
これらのコメントは、Go言語の io
パッケージにおける EOF
の慣習的な扱いを強調し、ReadFile
や ReadAll
のような「すべてを読み込む」関数が、その目的を達成した場合には nil
エラーを返すという期待される振る舞いをユーザーに伝えます。
関連リンク
- Go issue #2862: コミットメッセージに
Fixes #2862
と記載されていますが、現在のGoのGitHubリポジトリでこの番号のIssueを検索しても、このコミットと直接関連するIssueは見つかりませんでした。これは、Issueトラッカーの移行や、古いIssueがアーカイブされたことによるものかもしれません。
参考にした情報源リンク
- Go言語の
io
パッケージのドキュメント (一般的なio.Reader
とio.EOF
の振る舞いについて) - Go言語のエラーハンドリングに関する一般的な慣習
- このコミット自体の内容と差分