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

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

コミット

  • コミットハッシュ: 5bbdf40544008b78391cf2e6cfc37abe9a361b5d
  • 作者: Rob Pike r@golang.org
  • コミット日時: Thu Mar 21 14:56:42 2013 -0700

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

https://github.com/golang/go/commit/5bbdf40544008b78391cf2e6cfc37abe9a361b5d

元コミット内容

    bufio.Scanner: delete obsolete TODO
    Also fix the examples to use stderr for errors.
    
    R=golang-dev, bradfitz
    CC=golang-dev
    https://golang.org/cl/7716052

変更の背景

このコミットは、Go言語の標準ライブラリbufioパッケージ内のScannerに関する二つの改善を目的としています。

  1. TODOコメントの削除: src/pkg/bufio/scan.go内に存在していた「TODO(r): Provide executable examples.」というコメントが削除されています。これは、おそらくこのTODOが既に完了しているか、もはや関連性がなくなったため、コードベースのクリーンアップの一環として行われたと考えられます。不要なTODOコメントは、コードの可読性を低下させ、開発者が既に解決済みの問題に時間を費やす原因となるため、定期的な見直しと削除が推奨されます。

  2. エラー出力の修正: src/pkg/bufio/example_test.go内のExampleScanner_linesExampleScanner_wordsというテスト例において、エラーメッセージの出力先がos.Stdout(標準出力)からos.Stderr(標準エラー出力)に変更されています。これは、UNIX系のシステムにおける一般的な慣習に従うものです。プログラムの通常の出力は標準出力へ、エラーや診断メッセージは標準エラー出力へ送ることで、出力の分離と管理が容易になります。例えば、シェルスクリプトでプログラムの出力をパイプで別のコマンドに渡す際に、エラーメッセージが混入するのを防ぐことができます。

前提知識の解説

Go言語のbufioパッケージとScanner

bufioパッケージは、I/O操作を効率化するためのバッファリング機能を提供します。特にbufio.Scannerは、入力ストリーム(io.Readerインターフェースを実装する任意のデータソース、例えばファイルや標準入力)を、行、単語、またはカスタムの区切り文字でトークンに分割するための便利なインターフェースを提供します。

Scannerの主なメソッド:

  • Scan(): 次のトークン(デフォルトでは行)を読み込みます。読み込みが成功するとtrueを返し、EOFまたはエラーが発生するとfalseを返します。
  • Text(): Scan()が成功した後に、現在のトークンを文字列として返します。
  • Bytes(): Scan()が成功した後に、現在のトークンをバイトスライスとして返します。
  • Err(): Scan()falseを返した際に、発生したエラーを返します。エラーがなければnilを返します。

標準出力 (os.Stdout) と標準エラー出力 (os.Stderr)

UNIX系オペレーティングシステムでは、プロセスは通常、以下の3つの標準I/Oストリームを持っています。

  • 標準入力 (stdin): ファイルディスクリプタ0。プログラムへの入力に使用されます。Goではos.Stdinでアクセスできます。
  • 標準出力 (stdout): ファイルディスクリプタ1。プログラムの通常の出力に使用されます。Goではos.Stdoutでアクセスできます。
  • 標準エラー出力 (stderr): ファイルディスクリプタ2。プログラムのエラーメッセージや診断情報に使用されます。Goではos.Stderrでアクセスできます。

これらのストリームを適切に使い分けることは、プログラムの出力管理において非常に重要です。例えば、シェルでcommand > output.txtとすると標準出力のみがoutput.txtにリダイレクトされ、エラーメッセージは画面に表示されたままになります。command 2> error.txtとすると標準エラー出力のみがerror.txtにリダイレクトされます。

TODOコメント

プログラミングにおけるTODOコメントは、将来的に実装する必要がある機能、修正すべきバグ、または改善が必要なコード箇所を示すためにコード内に残されるメモです。通常、TODO:というプレフィックスが付けられ、その後に担当者名や日付、具体的な内容が記述されます。 しかし、TODOコメントは時間が経つと古くなったり、既に解決された問題を参照したりすることがあります。このような「obsolete TODO」は、コードベースのノイズとなり、開発者の混乱を招く可能性があるため、定期的に見直し、不要なものは削除することが良いプラクティスとされています。

技術的詳細

このコミットの技術的詳細は、Go言語のテストフレームワークにおけるExample関数の動作と、I/Oストリームの適切な利用に焦点を当てています。

Example関数の動作と出力の検証

Go言語のテストパッケージ(testing)では、Exampleというプレフィックスを持つ関数を記述することで、コードの実行例を示すことができます。これらの関数は、通常のテスト関数と同様にgo testコマンドで実行されますが、その出力は特別な方法で扱われます。

Example関数内に// Output:または// Unordered output:というコメントブロックが存在する場合、go testは関数の標準出力(os.Stdout)をキャプチャし、そのコメントブロックに記述された期待される出力と比較します。この比較が一致しない場合、テストは失敗します。

このコミットでfmt.Fprintln(os.Stdout, ...)からfmt.Fprintln(os.Stderr, ...)に変更されたのは、このExample関数の出力検証メカニズムと密接に関連しています。エラーメッセージをos.Stderrにリダイレクトすることで、Example関数の通常の出力(fmt.Println(scanner.Text())など)のみがos.Stdoutに送られ、// Output:コメントによる検証の対象となります。エラーメッセージはテストの出力検証プロセスから除外され、純粋な実行例の出力のみが比較されるようになります。これにより、テストの意図がより明確になり、エラーメッセージの有無や内容がExample関数の出力検証に影響を与えなくなります。

TODOコメントの削除

src/pkg/bufio/scan.goから削除されたTODO(r): Provide executable examples.というコメントは、bufio.Scannerのドキュメントに実行可能な例を追加する必要があることを示していました。このコミットが行われた時点で、example_test.goに既に実行可能な例(ExampleScanner_linesExampleScanner_wordsなど)が存在していたため、このTODOは「obsolete」(廃止された、時代遅れの)と判断され、削除されました。これは、コードベースの健全性を保つための一般的なメンテナンス作業です。

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

このコミットでは、以下の2つのファイルが変更されています。

  1. src/pkg/bufio/example_test.go

    • ExampleScanner_lines関数内で、エラー出力がos.Stdoutからos.Stderrに変更。
    • ExampleScanner_words関数内で、エラー出力がos.Stdoutからos.Stderrに変更。
  2. src/pkg/bufio/scan.go

    • Scanner構造体の定義の直前にあったTODOコメントが削除。

src/pkg/bufio/example_test.go の変更差分

--- a/src/pkg/bufio/example_test.go
+++ b/src/pkg/bufio/example_test.go
@@ -19,7 +19,7 @@ func ExampleScanner_lines() {
 		fmt.Println(scanner.Text()) // Println will add back the final '\n'
 	}
 	if err := scanner.Err(); err != nil {
-		fmt.Fprintln(os.Stdout, "reading standard input:", err)
+		fmt.Fprintln(os.Stderr, "reading standard input:", err)
 	}
 }
 
@@ -37,7 +37,7 @@ func ExampleScanner_words() {
 		count++
 	}
 	if err := scanner.Err(); err != nil {
-		fmt.Fprintln(os.Stdout, "reading input:", err)
+		fmt.Fprintln(os.Stderr, "reading input:", err)
 	}
 	fmt.Printf("%d\n", count)
 	// Output: 15

src/pkg/bufio/scan.go の変更差分

--- a/src/pkg/bufio/scan.go
+++ b/src/pkg/bufio/scan.go
@@ -27,8 +27,6 @@ import (
 // control over error handling or large tokens, or must run sequential scans
 // on a reader, should use bufio.Reader instead.
 //
-// TODO(r): Provide executable examples.
-//
 type Scanner struct {
 	r            io.Reader // The reader provided by the client.
 	split        SplitFunc // The function to split the tokens.

コアとなるコードの解説

src/pkg/bufio/example_test.go の変更解説

このファイルは、bufio.Scannerの使用例を示すテストコードを含んでいます。変更前は、scanner.Err()が返すエラーをos.Stdoutに出力していました。

// 変更前
if err := scanner.Err(); err != nil {
    fmt.Fprintln(os.Stdout, "reading standard input:", err)
}

変更後は、エラー出力がos.Stderrにリダイレクトされています。

// 変更後
if err := scanner.Err(); err != nil {
    fmt.Fprintln(os.Stderr, "reading standard input:", err)
}

この変更により、go testコマンドがExample関数を実行し、その出力を// Output:コメントと比較する際に、エラーメッセージが比較対象から除外されます。これにより、テストの意図(Scannerが正常にデータを処理した場合の出力例を示すこと)がより明確になり、エラーが発生した場合でもテストの期待される出力が安定します。これは、UNIXの哲学である「プログラムはエラーメッセージを標準エラー出力に、通常の出力を標準出力に書くべきである」という原則にも合致します。

src/pkg/bufio/scan.go の変更解説

このファイルはbufio.Scannerの実際のGo言語の実装を含んでいます。変更前は、Scanner構造体の定義の直前に以下のTODOコメントがありました。

// 変更前
// TODO(r): Provide executable examples.

このTODOコメントは、bufio.Scannerのドキュメントに実行可能な例を追加する必要があることを示していました。しかし、example_test.goに既に実行可能な例が追加されていたため、このTODOは不要となり、削除されました。

// 変更後: TODOコメントが削除された
type Scanner struct {
    r            io.Reader // The reader provided by the client.
    split        SplitFunc // The function to split the tokens.
    // ...
}

この変更は、コードベースのクリーンアップであり、既に完了したタスクを示す不要なコメントを削除することで、コードの保守性と可読性を向上させます。

関連リンク

参考にした情報源リンク