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

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

このコミットは、Go言語の標準ライブラリの複数のパッケージ(archive/zip, crypto/md5, crypto/sha1, encoding/json, errors, net, net/url, sync)に対して、多数のExample関数を追加するものです。これらのExample関数は、各パッケージの機能の使用方法を示す実行可能なドキュメントとして機能し、go testコマンドによって自動的にテストされます。

コミット

commit 3e804f98d75515bba73a86f563257eabceb1afe1
Author: Andrew Gerrand <adg@golang.org>
Date:   Sat Feb 18 11:48:33 2012 +1100

    pkg: a slew of examples

    R=golang-dev, gri, r
    CC=golang-dev
    https://golang.org/cl/5676071

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

https://github.com/golang/go/commit/3e804f98d75515bba73a86f563257eabceb1afe1

元コミット内容

pkg: a slew of examples

このコミットは、Go言語の標準パッケージに多数の例(Example)を追加することを目的としています。

変更の背景

Go言語では、Example関数は単なるコード例ではなく、go testコマンドによって実行され、その出力が期待される出力(// Output:コメントで指定)と一致するかどうかを検証するテストとしても機能します。これにより、コード例が常に最新かつ正確であることを保証し、開発者がライブラリの機能を理解し、適切に使用するための強力なドキュメントとなります。

このコミットの背景には、Go標準ライブラリのドキュメンテーションを強化し、ユーザーが各パッケージのAPIをより簡単に学習・利用できるようにするという意図があります。特に、新しいユーザーや特定の機能の使い方を知りたい開発者にとって、実行可能なコード例は非常に価値のある情報源となります。

前提知識の解説

Go言語のExample関数

Go言語のテストフレームワーク(testingパッケージ)には、通常のユニットテスト(TestXxx関数)やベンチマークテスト(BenchmarkXxx関数)に加えて、ExampleXxx関数という特別な種類の関数があります。

  • 目的: Example関数は、特定の関数、型、またはパッケージの使用方法を示すコード例を提供するために使用されます。
  • 配置: 通常、_test.goファイル内に配置されますが、通常のテスト関数とは異なり、Exampleというプレフィックスを持ちます。
  • 実行と検証: go testコマンドを実行すると、Example関数も実行されます。もしExample関数の最後に// Output:コメントが付いている場合、go testはそのExample関数の標準出力が// Output:コメントに続く内容と完全に一致するかどうかを検証します。一致しない場合、テストは失敗します。
  • ドキュメンテーション: go docコマンドやGoの公式ドキュメンテーションサイト(pkg.go.devなど)でパッケージのドキュメントを生成する際、Example関数のコードと出力は自動的にドキュメントに組み込まれ、ユーザーがコード例を直接参照できるようになります。

関連するGo標準ライブラリパッケージ

このコミットで例が追加された主なパッケージは以下の通りです。

  • archive/zip: ZIPアーカイブの読み書きを行うためのパッケージ。
  • crypto/md5: MD5ハッシュ関数を実装するパッケージ。
  • crypto/sha1: SHA-1ハッシュ関数を実装するパッケージ。
  • encoding/json: JSONエンコーディングとデコーディングをサポートするパッケージ。
  • errors: エラー処理のための基本的な機能を提供するパッケージ。
  • net: ネットワークI/Oのためのポータブルなインターフェースを提供するパッケージ。
  • net/url: URLの解析と生成を行うパッケージ。
  • sync: 低レベルの同期プリミティブ(ミューテックス、条件変数、WaitGroupなど)を提供するパッケージ。

これらのパッケージはGo言語の基本的な機能を提供しており、それぞれの使用例が追加されることで、Go開発者にとっての利便性が大幅に向上します。

技術的詳細

このコミットの技術的詳細は、主にGoのExample関数の実装と、それがどのようにドキュメンテーションとテストの両方に貢献するかという点に集約されます。

  1. Example関数の追加: 多くのパッケージで、ExampleXxxという命名規則に従った新しい関数が_test.goファイルに追加されています。これらの関数は、特定のAPI(例: zip.NewWriter, md5.New, json.NewDecoder, errors.New, net.Listen, url.Values, sync.WaitGroupなど)の典型的な使用シナリオを示しています。
  2. // Output:コメントによる出力検証: 追加されたExample関数の多くには、関数が標準出力に出力するであろう内容を正確に記述した// Output:コメントが含まれています。go testは、Example関数を実行し、その標準出力とこのコメントの内容を比較することで、コード例が期待通りに動作するかを検証します。これにより、コード例が古くなったり、バグを含んだりするリスクが低減されます。
  3. 既存のテストファイルへの追加と新規ファイルの作成:
    • crypto/md5/md5_test.go, crypto/sha1/sha1_test.go, encoding/json/example_test.go, errors/errors_test.go のように、既存のテストファイルにExample関数が追加されています。
    • archive/zip/example_test.go, errors/example_test.go, net/example_test.go, net/url/example_test.go, sync/example_test.go のように、example_test.goという新しいファイルが作成され、そこにExample関数がまとめられています。これは、通常のユニットテストとは異なる目的を持つExample関数を論理的に分離するための一般的なプラクティスです。
  4. パッケージ名の変更(一部): md5_test.gosha1_test.goのように、既存のテストファイルでパッケージ名がmd5からmd5_testへ、sha1からsha1_testへと変更されている箇所があります。これは、テスト対象のパッケージとは異なるパッケージとしてテストコードを記述する「外部テストパッケージ」の慣習に従うものです。これにより、テストコードがパッケージの外部からどのように見えるかをより正確にシミュレートできます。

これらの変更は、Go言語の「ドキュメントはコードであり、コードはドキュメントである」という哲学を体現しており、開発者がGoの標準ライブラリをより効果的に利用するための基盤を強化します。

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

このコミットでは、以下のファイルが新規作成または変更されています。

  • src/pkg/archive/zip/example_test.go (新規作成)
  • src/pkg/crypto/md5/md5_test.go (変更)
  • src/pkg/crypto/sha1/sha1_test.go (変更)
  • src/pkg/encoding/json/example_test.go (変更)
  • src/pkg/errors/errors_test.go (変更)
  • src/pkg/errors/example_test.go (新規作成)
  • src/pkg/net/example_test.go (新規作成)
  • src/pkg/net/url/example_test.go (新規作成)
  • src/pkg/sync/example_test.go (新規作成)

合計9ファイルが変更され、298行が追加、9行が削除されています。

コアとなるコードの解説

ここでは、いくつかの代表的なExample関数をピックアップして解説します。

src/pkg/archive/zip/example_test.goExampleWriterExampleReader

このファイルは、ZIPアーカイブの作成と読み込みの基本的な流れを示しています。

func ExampleWriter() {
	// Create a buffer to write our archive to.
	buf := new(bytes.Buffer)

	// Create a new zip archive.
	w := zip.NewWriter(buf)

	// Add some files to the archive.
	var files = []struct {
		Name, Body string
	}{
		{"readme.txt", "This archive contains some text files."},
		{"gopher.txt", "Gopher names:\nGeorge\nGeoffrey\nGonzo"},
		{"todo.txt", "Get animal handling licence.\nWrite more examples."},
	}
	for _, file := range files {
		f, err := w.Create(file.Name)
		if err != nil {
			log.Fatal(err)
		}
		_, err = f.Write([]byte(file.Body))
		if err != nil {
			log.Fatal(err)
		}
	}

	// Make sure to check the error on Close.
	err := w.Close()
	if err != nil {
		log.Fatal(err)
	}
}

func ExampleReader() {
	// Open a zip archive for reading.
	r, err := zip.OpenReader("testdata/readme.zip") // Note: This example assumes "testdata/readme.zip" exists.
	if err != nil {
		log.Fatal(err)
	}
	defer r.Close()

	// Iterate through the files in the archive,
	// printing some of their contents.
	for _, f := range r.File {
		fmt.Printf("Contents of %s:\n", f.Name)
		rc, err := f.Open()
		if err != nil {
			log.Fatal(err)
		}
		_, err = io.CopyN(os.Stdout, rc, 68) // Copies up to 68 bytes
		if err != nil {
			log.Fatal(err)
		}
		rc.Close()
		fmt.Println()
	}
	// Output:
	// Contents of README:
	// This is the source code repository for the Go programming language.
}
  • ExampleWriter: bytes.Bufferを使ってインメモリでZIPアーカイブを作成し、複数のテキストファイルをアーカイブに追加する手順を示しています。zip.NewWriterでライターを作成し、w.Createで各ファイルのエントリを作成、f.Writeで内容を書き込み、最後にw.Close()でアーカイブを閉じます。
  • ExampleReader: 既存のZIPファイル(testdata/readme.zip)を開き、その中のファイルをイテレートして内容の一部を読み出す方法を示しています。zip.OpenReaderでリーダーを開き、r.Fileでファイルリストを取得、各ファイルのf.Open()でリーダーを取得し、io.CopyNで内容を標準出力にコピーしています。// Output:コメントにより、特定の出力が期待されていることが示されています。

src/pkg/crypto/md5/md5_test.goExampleNew

MD5ハッシュの計算方法を示しています。

func ExampleNew() {
	h := md5.New()
	io.WriteString(h, "The fog is getting thicker!")
	io.WriteString(h, "And Leon's getting laaarger!")
	fmt.Printf("%x", h.Sum(nil))
	// Output: e2c569be17396eca2a2e3c11578123ed
}
  • md5.New()で新しいMD5ハッシュオブジェクトを作成し、io.WriteStringでデータを書き込みます。
  • h.Sum(nil)で最終的なハッシュ値を取得し、fmt.Printf("%x", ...)で16進数文字列として出力しています。
  • // Output:コメントにより、このコードが特定のMD5ハッシュ値を生成することが検証されます。

src/pkg/errors/errors_test.goExampleNewExampleNew_errorf

errorsパッケージの基本的なエラー作成方法と、fmt.Errorfを使ったより柔軟なエラー作成方法を示しています。

func ExampleNew() {
	err := errors.New("emit macho dwarf: elf header corrupted")
	if err != nil {
		fmt.Print(err)
	}
	// Output: emit macho dwarf: elf header corrupted
}

// The fmt package's Errorf function lets us use the package's formatting
// features to create descriptive error messages.
func ExampleNew_errorf() {
	const name, id = "bimmler", 17
	err := fmt.Errorf("user %q (id %d) not found", name, id)
	if err != nil {
		fmt.Print(err)
	}
	// Output: user "bimmler" (id 17) not found
}
  • ExampleNew: errors.Newを使ってシンプルなエラー文字列を作成し、出力しています。
  • ExampleNew_errorf: fmt.Errorfを使ってフォーマットされた文字列からエラーを作成する方法を示しています。これは、動的な情報を含むエラーメッセージを生成する際によく使われます。

これらの例は、Go言語のAPIがどのように設計され、どのように使用されるべきかを示すための明確で実行可能なガイドラインを提供します。

関連リンク

  • Go言語のtestingパッケージのドキュメント: https://pkg.go.dev/testing
  • Go言語のExample関数に関する公式ブログ記事やドキュメント(当時の情報源に近いもの):
    • Go Blog: https://go.dev/blog/ (具体的な記事はコミット当時のものを見つけるのが難しいですが、Example関数に関する情報はGoのドキュメンテーションの重要な一部です。)

参考にした情報源リンク

  • Go言語の公式ドキュメンテーション (pkg.go.dev)
  • Go言語のGitHubリポジトリ (golang/go)
  • コミットログと差分情報 (/home/orange/Project/comemo/commit_data/12021.txt)