Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

[インデックス 19108] ファイルの概要

このコミットは、Go言語の標準ライブラリであるbytesパッケージとstringsパッケージ内のReader型が返すエラーメッセージの整合性を向上させることを目的としています。具体的には、エラーメッセージにどのメソッドから発生したエラーであるかを示す情報を追加することで、デバッグ時の可読性と問題特定を容易にしています。

コミット

commit 08d8eca9680d22e3229bac06b23852542340d6cf
Author: Robert Griesemer <gri@golang.org>
Date:   Thu Apr 10 21:45:41 2014 -0700

    bytes, strings: more consistent error messages

    LGTM=bradfitz
    R=bradfitz
    CC=golang-codereviews
    https://golang.org/cl/86060044

GitHub上でのコミットページへのリンク

https://github.com/golang/go/commit/08d8eca9680d22e3229bac06b23852542340d6cf

元コミット内容

bytes, strings: more consistent error messages

このコミットは、bytesパッケージとstringsパッケージにおいて、より一貫性のあるエラーメッセージを提供する変更です。

変更の背景

Go言語の標準ライブラリでは、エラーハンドリングは非常に重要な要素です。エラーメッセージは、プログラムが予期せぬ状態に陥った際に、開発者が問題の原因を迅速に特定するための重要な手がかりとなります。このコミット以前は、bytes.Readerstrings.Readerの特定のメソッドが返すエラーメッセージが、単に「bytes: invalid offset」や「strings: negative position」といった汎用的なものでした。

このような汎用的なエラーメッセージでは、エラーが発生した具体的なメソッドを特定することが困難であり、特に複雑なコードベースや複数のReaderインスタンスが関与する場合には、デバッグの効率を低下させる可能性がありました。

この変更の背景には、Go言語の設計哲学の一つである「明確さ」と「実用性」があります。エラーメッセージをより詳細に、かつ一貫性のある形式にすることで、ライブラリの利用者にとっての使いやすさとデバッグ体験を向上させることが目的でした。具体的には、エラーメッセージにbytes.Reader.ReadAtのように、エラーを発生させた型とメソッドの名前を含めることで、エラーの発生源を明確にし、デバッグの労力を削減しようとしています。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念と標準ライブラリの知識が必要です。

  • io.Readerインターフェース: Go言語におけるデータの読み込み操作を抽象化する基本的なインターフェースです。Read(p []byte) (n int, err error)メソッドを定義しており、バイトスライスpにデータを読み込み、読み込んだバイト数nとエラーerrを返します。多くのI/O操作の基盤となります。

  • io.ReaderAtインターフェース: io.Readerを拡張し、特定のオフセットからデータを読み込む機能を提供するインターフェースです。ReadAt(p []byte, off int64) (n int, err error)メソッドを定義しており、offで指定されたオフセットからデータを読み込みます。ファイルやメモリ上のデータなど、ランダムアクセスが可能なデータソースで利用されます。

  • bytes.Reader: bytesパッケージに含まれる型で、バイトスライス([]byte)をio.Readerio.ReaderAtio.Seekerio.ByteReaderio.RuneReaderインターフェースとして扱うための実装です。メモリ上のバイトデータをファイルのように読み書きする際に便利です。

  • strings.Reader: stringsパッケージに含まれる型で、文字列(string)をio.Readerio.ReaderAtio.Seekerio.ByteReaderio.RuneReaderインターフェースとして扱うための実装です。文字列データをストリームとして読み込む際に使用されます。

  • errors.New関数: errorsパッケージの関数で、新しいエラーを生成するために使用されます。引数として文字列を受け取り、その文字列をエラーメッセージとするerror型の値を返します。Go言語でカスタムエラーを返す際の一般的な方法です。

  • エラーメッセージの重要性: Go言語では、エラーは戻り値として明示的に扱われます。エラーメッセージは、エラーが発生した状況や原因を説明するテキスト情報であり、プログラムのデバッグや運用において非常に重要です。一貫性があり、情報量の多いエラーメッセージは、問題解決の時間を大幅に短縮します。

  • Seekメソッドとwhence: io.Seekerインターフェースの一部であるSeek(offset int64, whence int) (int64, error)メソッドは、読み書き位置を変更するために使用されます。whence引数は、offsetの基準位置を指定します。

    • io.SeekStart (0): ファイルの先頭からのオフセット
    • io.SeekCurrent (1): 現在の読み書き位置からのオフセット
    • io.SeekEnd (2): ファイルの末尾からのオフセット

これらの知識があることで、コミットがbytes.Readerstrings.Readerの特定のメソッドが返すエラーメッセージを、より具体的でデバッグしやすい形式に変更していることが理解できます。

技術的詳細

このコミットの技術的な詳細は、Go言語におけるエラーメッセージの設計と、bytesおよびstringsパッケージのReader実装におけるエラーハンドリングの改善に焦点を当てています。

Go言語のエラーは、通常、errorインターフェースを実装する型によって表現されます。最も一般的なエラーの作成方法は、errors.New("エラーメッセージ")を使用することです。このコミットでは、既存のerrors.New呼び出しを変更し、エラーメッセージの文字列リテラルを修正しています。

変更のパターンは以下の通りです。

  1. 汎用的なエラーメッセージから具体的なエラーメッセージへの変更: 変更前: errors.New("bytes: invalid offset") 変更後: errors.New("bytes.Reader.ReadAt: negative offset")

    この変更により、エラーメッセージには以下の情報が含まれるようになります。

    • パッケージ名: bytesまたはstrings
    • 型名: Reader
    • メソッド名: ReadAt, UnreadByte, UnreadRune, Seek
    • 具体的なエラー内容: negative offset, at beginning of slice, previous operation was not ReadRune, invalid whence, negative position
  2. 影響を受けるメソッド:

    • ReadAt: 負のオフセットが指定された場合のエラー。
    • UnreadByte: 読み込み位置が先頭にある状態でバイトをアンリードしようとした場合のエラー。
    • UnreadRune: 直前の操作がReadRuneでなかった場合にルーンをアンリードしようとした場合のエラー。
    • Seek: 無効なwhence値が指定された場合、または結果として負の読み書き位置になる場合のエラー。
  3. テストコードの更新: エラーメッセージの変更に伴い、bytes/reader_test.gostrings/reader_test.go内の対応するテストケースも更新されています。具体的には、seekerrフィールドや期待されるエラー文字列が、新しい詳細なエラーメッセージと一致するように修正されています。これにより、変更が正しく反映され、将来的な回帰を防ぐことができます。

このアプローチは、Go言語の標準ライブラリ全体で推奨されるエラーメッセージのスタイルに沿ったものです。エラーメッセージにコンテキスト(どのパッケージ、どの型、どのメソッドでエラーが発生したか)を含めることで、ライブラリの利用者がエラーをより迅速に診断し、解決できるようになります。これは、ライブラリのAPI設計における「使いやすさ」と「デバッグの容易さ」を向上させるための重要な改善です。

コアとなるコードの変更箇所

このコミットでは、主にsrc/pkg/bytes/reader.gosrc/pkg/strings/reader.goの2つのファイルが変更されています。それぞれのファイルで、エラーを生成するerrors.Newの引数文字列が修正されています。

src/pkg/bytes/reader.go

--- a/src/pkg/bytes/reader.go
+++ b/src/pkg/bytes/reader.go
@@ -45,7 +45,7 @@ func (r *Reader) Read(b []byte) (n int, err error) {
 func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {
 	// cannot modify state - see io.ReaderAt
 	if off < 0 {
-		return 0, errors.New("bytes: invalid offset")
+		return 0, errors.New("bytes.Reader.ReadAt: negative offset")
 	}
 	if off >= int64(len(r.s)) {
 		return 0, io.EOF
@@ -68,11 +68,11 @@ func (r *Reader) ReadByte() (b byte, err error) {
 }
 
 func (r *Reader) UnreadByte() error {
+	r.prevRune = -1
 	if r.i <= 0 {
-		return errors.New("bytes.Reader: at beginning of slice")
+		return errors.New("bytes.Reader.UnreadByte: at beginning of slice")
 	}
 	r.i--
-	r.prevRune = -1
 	return nil
 }
 
@@ -93,7 +93,7 @@ func (r *Reader) ReadRune() (ch rune, size int, err error) {
 
 func (r *Reader) UnreadRune() error {
 	if r.prevRune < 0 {
-		return errors.New("bytes.Reader: previous operation was not ReadRune")
+		return errors.New("bytes.Reader.UnreadRune: previous operation was not ReadRune")
 	}
 	r.i = int64(r.prevRune)
 	r.prevRune = -1
@@ -112,10 +112,10 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {\n 	case 2:\n 		abs = int64(len(r.s)) + offset\n 	default:\
-		return 0, errors.New("bytes: invalid whence")
+		return 0, errors.New("bytes.Reader.Seek: invalid whence")
 	}\
 	if abs < 0 {\
-		return 0, errors.New("bytes: negative position")
+		return 0, errors.New("bytes.Reader.Seek: negative position")
 	}\
 	r.i = abs
 	return abs, nil

src/pkg/strings/reader.go

--- a/src/pkg/strings/reader.go
+++ b/src/pkg/strings/reader.go
@@ -44,7 +44,7 @@ func (r *Reader) Read(b []byte) (n int, err error) {
 func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {
 	// cannot modify state - see io.ReaderAt
 	if off < 0 {
-		return 0, errors.New("strings: invalid offset")
+		return 0, errors.New("strings.Reader.ReadAt: negative offset")
 	}
 	if off >= int64(len(r.s)) {
 		return 0, io.EOF
@@ -67,11 +67,11 @@ func (r *Reader) ReadByte() (b byte, err error) {
 }
 
 func (r *Reader) UnreadByte() error {
+	r.prevRune = -1
 	if r.i <= 0 {
-		return errors.New("strings.Reader: at beginning of string")
+		return errors.New("strings.Reader.UnreadByte: at beginning of string")
 	}
 	r.i--
-	r.prevRune = -1
 	return nil
 }
 
@@ -92,7 +92,7 @@ func (r *Reader) ReadRune() (ch rune, size int, err error) {
 
 func (r *Reader) UnreadRune() error {
 	if r.prevRune < 0 {
-		return errors.New("strings.Reader: previous operation was not ReadRune")
+		return errors.New("strings.Reader.UnreadRune: previous operation was not ReadRune")
 	}
 	r.i = int64(r.prevRune)
 	r.prevRune = -1
@@ -111,10 +111,10 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {\n 	case 2:\n 		abs = int64(len(r.s)) + offset\n 	default:\
-		return 0, errors.New("strings: invalid whence")
+		return 0, errors.New("strings.Reader.Seek: invalid whence")
 	}\
 	if abs < 0 {\
-		return 0, errors.New("strings: negative position")
+		return 0, errors.New("strings.Reader.Seek: negative position")
 	}\
 	r.i = abs
 	return abs, nil

また、これらの変更に合わせて、テストファイルも更新されています。

src/pkg/bytes/reader_test.go

--- a/src/pkg/bytes/reader_test.go
+++ b/src/pkg/bytes/reader_test.go
@@ -27,7 +27,7 @@ func TestReader(t *testing.T) {
 		{seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
 		{seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
 		{seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
-		{seek: os.SEEK_SET, off: -1, seekerr: "bytes: negative position"},
+		{seek: os.SEEK_SET, off: -1, seekerr: "bytes.Reader.Seek: negative position"},
 		{seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
 		{seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
 		{seek: os.SEEK_SET, n: 5, want: "01234"},
@@ -84,7 +84,7 @@ func TestReaderAt(t *testing.T) {
 		{1, 9, "123456789", nil},
 		{11, 10, "", io.EOF},
 		{0, 0, "", nil},
-		{-1, 0, "", "bytes: invalid offset"},
+		{-1, 0, "", "bytes.Reader.ReadAt: negative offset"},
 	}
 	for i, tt := range tests {
 		b := make([]byte, tt.n)

src/pkg/strings/reader_test.go

--- a/src/pkg/strings/reader_test.go
+++ b/src/pkg/strings/reader_test.go
@@ -27,7 +27,7 @@ func TestReader(t *testing.T) {
 		{seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
 		{seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
 		{seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
-		{seek: os.SEEK_SET, off: -1, seekerr: "strings: negative position"},
+		{seek: os.SEEK_SET, off: -1, seekerr: "strings.Reader.Seek: negative position"},
 		{seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
 		{seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
 		{seek: os.SEEK_SET, n: 5, want: "01234"},
@@ -84,7 +84,7 @@ func TestReaderAt(t *testing.T) {
 		{1, 9, "123456789", nil},
 		{11, 10, "", io.EOF},
 		{0, 0, "", nil},
-		{-1, 0, "", "strings: invalid offset"},
+		{-1, 0, "", "strings.Reader.ReadAt: negative offset"},
 	}
 	for i, tt := range tests {
 		b := make([]byte, tt.n)

コアとなるコードの解説

このコミットのコアとなる変更は、errors.New関数に渡される文字列リテラルを修正することです。これにより、エラーメッセージのフォーマットが改善され、エラーの発生源がより明確になります。

例えば、bytes.Reader.ReadAtメソッドでは、負のオフセットが指定された場合にエラーを返します。変更前は、このエラーメッセージは単に"bytes: invalid offset"でした。これは、bytesパッケージ内で無効なオフセットが発生したことは示しますが、具体的にどの型やメソッドで問題が起きたのかは不明瞭でした。

変更後、エラーメッセージは"bytes.Reader.ReadAt: negative offset"となります。この新しいメッセージは、以下の情報を提供します。

  • bytes.Reader: エラーがbytesパッケージのReader型から発生したことを示します。
  • ReadAt: エラーがReadAtメソッド内で発生したことを示します。
  • negative offset: エラーの具体的な原因が負のオフセットであること。

同様の変更が、UnreadByteUnreadRuneSeekメソッドにも適用されています。

  • UnreadByteのエラーメッセージ: "bytes.Reader.UnreadByte: at beginning of slice" (またはstrings.Readerの場合)
  • UnreadRuneのエラーメッセージ: "bytes.Reader.UnreadRune: previous operation was not ReadRune" (またはstrings.Readerの場合)
  • Seekのエラーメッセージ: "bytes.Reader.Seek: invalid whence" または "bytes.Reader.Seek: negative position" (またはstrings.Readerの場合)

これらの変更は、Go言語のエラーハンドリングのベストプラクティスに沿ったものです。エラーメッセージに十分なコンテキストを含めることで、開発者はエラーログを解析する際に、どのコードパスで問題が発生したのかを迅速に特定できます。これにより、デバッグの効率が向上し、問題解決までの時間を短縮できます。

また、UnreadByteメソッドの変更では、r.prevRune = -1の行がif r.i <= 0の条件文の外に移動しています。これは、UnreadByteが成功した場合でもprevRuneをリセットすることで、UnreadRuneが誤って呼び出されるのを防ぐための小さな改善です。ただし、このコミットの主要な目的はエラーメッセージの整合性向上にあります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード (特にsrc/pkg/bytessrc/pkg/stringsディレクトリ)
  • Go言語のコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/86060044 (コミットメッセージに記載されているリンク)
  • Go言語のエラーハンドリングに関する一般的な情報源 (Goブログなど)
I have generated the detailed explanation in Markdown format, including all the required sections and adhering to the specified instructions. The output is to standard output only.```markdown
# [インデックス 19108] ファイルの概要

このコミットは、Go言語の標準ライブラリである`bytes`パッケージと`strings`パッケージ内の`Reader`型が返すエラーメッセージの整合性を向上させることを目的としています。具体的には、エラーメッセージにどのメソッドから発生したエラーであるかを示す情報を追加することで、デバッグ時の可読性と問題特定を容易にしています。

## コミット

commit 08d8eca9680d22e3229bac06b23852542340d6cf Author: Robert Griesemer gri@golang.org Date: Thu Apr 10 21:45:41 2014 -0700

bytes, strings: more consistent error messages

LGTM=bradfitz
R=bradfitz
CC=golang-codereviews
https://golang.org/cl/86060044

## GitHub上でのコミットページへのリンク

[https://github.com/golang/go/commit/08d8eca9680d22e3229bac06b23852542340d6cf](https://github.com/golang/go/commit/08d8eca9680d22e3229bac06b23852542340d6cf)

## 元コミット内容

`bytes, strings: more consistent error messages`

このコミットは、`bytes`パッケージと`strings`パッケージにおいて、より一貫性のあるエラーメッセージを提供する変更です。

## 変更の背景

Go言語の標準ライブラリでは、エラーハンドリングは非常に重要な要素です。エラーメッセージは、プログラムが予期せぬ状態に陥った際に、開発者が問題の原因を迅速に特定するための重要な手がかりとなります。このコミット以前は、`bytes.Reader`や`strings.Reader`の特定のメソッドが返すエラーメッセージが、単に「bytes: invalid offset」や「strings: negative position」といった汎用的なものでした。

このような汎用的なエラーメッセージでは、エラーが発生した具体的なメソッドを特定することが困難であり、特に複雑なコードベースや複数の`Reader`インスタンスが関与する場合には、デバッグの効率を低下させる可能性がありました。

この変更の背景には、Go言語の設計哲学の一つである「明確さ」と「実用性」があります。エラーメッセージをより詳細に、かつ一貫性のある形式にすることで、ライブラリの利用者にとっての使いやすさとデバッグ体験を向上させることが目的でした。具体的には、エラーメッセージに`bytes.Reader.ReadAt`のように、エラーを発生させた型とメソッドの名前を含めることで、エラーの発生源を明確にし、デバッグの労力を削減しようとしています。

## 前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念と標準ライブラリの知識が必要です。

*   **`io.Reader`インターフェース**:
    Go言語におけるデータの読み込み操作を抽象化する基本的なインターフェースです。`Read(p []byte) (n int, err error)`メソッドを定義しており、バイトスライス`p`にデータを読み込み、読み込んだバイト数`n`とエラー`err`を返します。多くのI/O操作の基盤となります。

*   **`io.ReaderAt`インターフェース**:
    `io.Reader`を拡張し、特定のオフセットからデータを読み込む機能を提供するインターフェースです。`ReadAt(p []byte, off int64) (n int, err error)`メソッドを定義しており、`off`で指定されたオフセットからデータを読み込みます。ファイルやメモリ上のデータなど、ランダムアクセスが可能なデータソースで利用されます。

*   **`bytes.Reader`型**:
    `bytes`パッケージに含まれる型で、バイトスライス(`[]byte`)を`io.Reader`、`io.ReaderAt`、`io.Seeker`、`io.ByteReader`、`io.RuneReader`インターフェースとして扱うための実装です。メモリ上のバイトデータをファイルのように読み書きする際に便利です。

*   **`strings.Reader`型**:
    `strings`パッケージに含まれる型で、文字列(`string`)を`io.Reader`、`io.ReaderAt`、`io.Seeker`、`io.ByteReader`、`io.RuneReader`インターフェースとして扱うための実装です。文字列データをストリームとして読み込む際に使用されます。

*   **`errors.New`関数**:
    `errors`パッケージの関数で、新しいエラーを生成するために使用されます。引数として文字列を受け取り、その文字列をエラーメッセージとする`error`型の値を返します。Go言語でカスタムエラーを返す際の一般的な方法です。

*   **エラーメッセージの重要性**:
    Go言語では、エラーは戻り値として明示的に扱われます。エラーメッセージは、エラーが発生した状況や原因を説明するテキスト情報であり、プログラムのデバッグや運用において非常に重要です。一貫性があり、情報量の多いエラーメッセージは、問題解決の時間を大幅に短縮します。

*   **`Seek`メソッドと`whence`**:
    `io.Seeker`インターフェースの一部である`Seek(offset int64, whence int) (int64, error)`メソッドは、読み書き位置を変更するために使用されます。`whence`引数は、`offset`の基準位置を指定します。
    *   `io.SeekStart` (0): ファイルの先頭からのオフセット
    *   `io.SeekCurrent` (1): 現在の読み書き位置からのオフセット
    *   `io.SeekEnd` (2): ファイルの末尾からのオフセット

これらの知識があることで、コミットが`bytes.Reader`と`strings.Reader`の特定のメソッドが返すエラーメッセージを、より具体的でデバッグしやすい形式に変更していることが理解できます。

## 技術的詳細

このコミットの技術的な詳細は、Go言語におけるエラーメッセージの設計と、`bytes`および`strings`パッケージの`Reader`実装におけるエラーハンドリングの改善に焦点を当てています。

Go言語のエラーは、通常、`error`インターフェースを実装する型によって表現されます。最も一般的なエラーの作成方法は、`errors.New("エラーメッセージ")`を使用することです。このコミットでは、既存の`errors.New`呼び出しを変更し、エラーメッセージの文字列リテラルを修正しています。

変更のパターンは以下の通りです。

1.  **汎用的なエラーメッセージから具体的なエラーメッセージへの変更**:
    変更前: `errors.New("bytes: invalid offset")`
    変更後: `errors.New("bytes.Reader.ReadAt: negative offset")`

    この変更により、エラーメッセージには以下の情報が含まれるようになります。
    *   **パッケージ名**: `bytes`または`strings`
    *   **型名**: `Reader`
    *   **メソッド名**: `ReadAt`, `UnreadByte`, `UnreadRune`, `Seek`
    *   **具体的なエラー内容**: `negative offset`, `at beginning of slice`, `previous operation was not ReadRune`, `invalid whence`, `negative position`

2.  **影響を受けるメソッド**:
    *   `ReadAt`: 負のオフセットが指定された場合のエラー。
    *   `UnreadByte`: 読み込み位置が先頭にある状態でバイトをアンリードしようとした場合のエラー。
    *   `UnreadRune`: 直前の操作が`ReadRune`でなかった場合にルーンをアンリードしようとした場合のエラー。
    *   `Seek`: 無効な`whence`値が指定された場合、または結果として負の読み書き位置になる場合のエラー。

3.  **テストコードの更新**:
    エラーメッセージの変更に伴い、`bytes/reader_test.go`と`strings/reader_test.go`内の対応するテストケースも更新されています。具体的には、`seekerr`フィールドや期待されるエラー文字列が、新しい詳細なエラーメッセージと一致するように修正されています。これにより、変更が正しく反映され、将来的な回帰を防ぐことができます。

このアプローチは、Go言語の標準ライブラリ全体で推奨されるエラーメッセージのスタイルに沿ったものです。エラーメッセージにコンテキスト(どのパッケージ、どの型、どのメソッドでエラーが発生したか)を含めることで、ライブラリの利用者がエラーをより迅速に診断し、解決できるようになります。これは、ライブラリのAPI設計における「使いやすさ」と「デバッグの容易さ」を向上させるための重要な改善です。

## コアとなるコードの変更箇所

このコミットでは、主に`src/pkg/bytes/reader.go`と`src/pkg/strings/reader.go`の2つのファイルが変更されています。それぞれのファイルで、エラーを生成する`errors.New`の引数文字列が修正されています。

### `src/pkg/bytes/reader.go`

```diff
--- a/src/pkg/bytes/reader.go
+++ b/src/pkg/bytes/reader.go
@@ -45,7 +45,7 @@ func (r *Reader) Read(b []byte) (n int, err error) {
 func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {
 	// cannot modify state - see io.ReaderAt
 	if off < 0 {
-		return 0, errors.New("bytes: invalid offset")
+		return 0, errors.New("bytes.Reader.ReadAt: negative offset")
 	}
 	if off >= int64(len(r.s)) {
 		return 0, io.EOF
@@ -68,11 +68,11 @@ func (r *Reader) ReadByte() (b byte, err error) {
 }
 
 func (r *Reader) UnreadByte() error {
+	r.prevRune = -1
 	if r.i <= 0 {
-		return errors.New("bytes.Reader: at beginning of slice")
+		return errors.New("bytes.Reader.UnreadByte: at beginning of slice")
 	}
 	r.i--
-	r.prevRune = -1
 	return nil
 }
 
@@ -93,7 +93,7 @@ func (r *Reader) ReadRune() (ch rune, size int, err error) {
 
 func (r *Reader) UnreadRune() error {
 	if r.prevRune < 0 {
-		return errors.New("bytes.Reader: previous operation was not ReadRune")
+		return 0, errors.New("bytes.Reader.UnreadRune: previous operation was not ReadRune")
 	}
 	r.i = int64(r.prevRune)
 	r.prevRune = -1
@@ -112,10 +112,10 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {\n 	case 2:\n 		abs = int64(len(r.s)) + offset\n 	default:\
-		return 0, errors.New("bytes: invalid whence")
+		return 0, errors.New("bytes.Reader.Seek: invalid whence")
 	}\
 	if abs < 0 {\
-		return 0, errors.New("bytes: negative position")
+		return 0, errors.New("bytes.Reader.Seek: negative position")
 	}\
 	r.i = abs
 	return abs, nil

src/pkg/strings/reader.go

--- a/src/pkg/strings/reader.go
+++ b/src/pkg/strings/reader.go
@@ -44,7 +44,7 @@ func (r *Reader) Read(b []byte) (n int, err error) {
 func (r *Reader) ReadAt(b []byte, off int64) (n int, err error) {
 	// cannot modify state - see io.ReaderAt
 	if off < 0 {
-		return 0, errors.New("strings: invalid offset")
+		return 0, errors.New("strings.Reader.ReadAt: negative offset")
 	}
 	if off >= int64(len(r.s)) {
 		return 0, io.EOF
@@ -67,11 +67,11 @@ func (r *Reader) ReadByte() (b byte, err error) {
 }
 
 func (r *Reader) UnreadByte() error {
+	r.prevRune = -1
 	if r.i <= 0 {
-		return errors.New("strings.Reader: at beginning of string")
+		return 0, errors.New("strings.Reader.UnreadByte: at beginning of string")
 	}
 	r.i--
-	r.prevRune = -1
 	return nil
 }
 
@@ -92,7 +92,7 @@ func (r *Reader) ReadRune() (ch rune, size int, err error) {
 
 func (r *Reader) UnreadRune() error {
 	if r.prevRune < 0 {
-		return errors.New("strings.Reader: previous operation was not ReadRune")
+		return 0, errors.New("strings.Reader.UnreadRune: previous operation was not ReadRune")
 	}
 	r.i = int64(r.prevRune)
 	r.prevRune = -1
@@ -111,10 +111,10 @@ func (r *Reader) Seek(offset int64, whence int) (int64, error) {\n 	case 2:\n 		abs = int64(len(r.s)) + offset\n 	default:\
-		return 0, errors.New("strings: invalid whence")
+		return 0, errors.New("strings.Reader.Seek: invalid whence")
 	}\
 	if abs < 0 {\
-		return 0, errors.New("strings: negative position")
+		return 0, errors.New("strings.Reader.Seek: negative position")
 	}\
 	r.i = abs
 	return abs, nil

また、これらの変更に合わせて、テストファイルも更新されています。

src/pkg/bytes/reader_test.go

--- a/src/pkg/bytes/reader_test.go
+++ b/src/pkg/bytes/reader_test.go
@@ -27,7 +27,7 @@ func TestReader(t *testing.T) {
 		{seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
 		{seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
 		{seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
-		{seek: os.SEEK_SET, off: -1, seekerr: "bytes: negative position"},
+		{seek: os.SEEK_SET, off: -1, seekerr: "bytes.Reader.Seek: negative position"},
 		{seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
 		{seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
 		{seek: os.SEEK_SET, n: 5, want: "01234"},
@@ -84,7 +84,7 @@ func TestReaderAt(t *testing.T) {
 		{1, 9, "123456789", nil},
 		{11, 10, "", io.EOF},
 		{0, 0, "", nil},
-		{-1, 0, "", "bytes: invalid offset"},
+		{-1, 0, "", "bytes.Reader.ReadAt: negative offset"},
 	}
 	for i, tt := range tests {
 		b := make([]byte, tt.n)

src/pkg/strings/reader_test.go

--- a/src/pkg/strings/reader_test.go
+++ b/src/pkg/strings/reader_test.go
@@ -27,7 +27,7 @@ func TestReader(t *testing.T) {
 		{seek: os.SEEK_SET, off: 0, n: 20, want: "0123456789"},
 		{seek: os.SEEK_SET, off: 1, n: 1, want: "1"},
 		{seek: os.SEEK_CUR, off: 1, wantpos: 3, n: 2, want: "34"},
-		{seek: os.SEEK_SET, off: -1, seekerr: "strings: negative position"},
+		{seek: os.SEEK_SET, off: -1, seekerr: "strings.Reader.Seek: negative position"},
 		{seek: os.SEEK_SET, off: 1 << 33, wantpos: 1 << 33},
 		{seek: os.SEEK_CUR, off: 1, wantpos: 1<<33 + 1},
 		{seek: os.SEEK_SET, n: 5, want: "01234"},
@@ -84,7 +84,7 @@ func TestReaderAt(t *testing.T) {
 		{1, 9, "123456789", nil},
 		{11, 10, "", io.EOF},
 		{0, 0, "", nil},
-		{-1, 0, "", "strings: invalid offset"},
+		{-1, 0, "", "strings.Reader.ReadAt: negative offset"},
 	}
 	for i, tt := range tests {
 		b := make([]byte, tt.n)

コアとなるコードの解説

このコミットのコアとなる変更は、errors.New関数に渡される文字列リテラルを修正することです。これにより、エラーメッセージのフォーマットが改善され、エラーの発生源がより明確になります。

例えば、bytes.Reader.ReadAtメソッドでは、負のオフセットが指定された場合にエラーを返します。変更前は、このエラーメッセージは単に"bytes: invalid offset"でした。これは、bytesパッケージ内で無効なオフセットが発生したことは示しますが、具体的にどの型やメソッドで問題が起きたのかは不明瞭でした。

変更後、エラーメッセージは"bytes.Reader.ReadAt: negative offset"となります。この新しいメッセージは、以下の情報を提供します。

  • bytes.Reader: エラーがbytesパッケージのReader型から発生したことを示します。
  • ReadAt: エラーがReadAtメソッド内で発生したことを示します。
  • negative offset: エラーの具体的な原因が負のオフセットであること。

同様の変更が、UnreadByteUnreadRuneSeekメソッドにも適用されています。

  • UnreadByteのエラーメッセージ: "bytes.Reader.UnreadByte: at beginning of slice" (またはstrings.Readerの場合)
  • UnreadRuneのエラーメッセージ: "bytes.Reader.UnreadRune: previous operation was not ReadRune" (またはstrings.Readerの場合)
  • Seekのエラーメッセージ: "bytes.Reader.Seek: invalid whence" または "bytes.Reader.Seek: negative position" (またはstrings.Readerの場合)

これらの変更は、Go言語のエラーハンドリングのベストプラクティスに沿ったものです。エラーメッセージに十分なコンテキストを含めることで、開発者はエラーログを解析する際に、どのコードパスで問題が発生したのかを迅速に特定できます。これにより、デバッグの効率が向上し、問題解決までの時間を短縮できます。

また、UnreadByteメソッドの変更では、r.prevRune = -1の行がif r.i <= 0の条件文の外に移動しています。これは、UnreadByteが成功した場合でもprevRuneをリセットすることで、UnreadRuneが誤って呼び出されるのを防ぐための小さな改善です。ただし、このコミットの主要な目的はエラーメッセージの整合性向上にあります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード (特にsrc/pkg/bytessrc/pkg/stringsディレクトリ)
  • Go言語のコードレビューシステム (Gerrit) の変更リスト: https://golang.org/cl/86060044 (コミットメッセージに記載されているリンク)
  • Go言語のエラーハンドリングに関する一般的な情報源 (Goブログなど)