[インデックス 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
関数の実装と、それがどのようにドキュメンテーションとテストの両方に貢献するかという点に集約されます。
Example
関数の追加: 多くのパッケージで、ExampleXxx
という命名規則に従った新しい関数が_test.go
ファイルに追加されています。これらの関数は、特定のAPI(例:zip.NewWriter
,md5.New
,json.NewDecoder
,errors.New
,net.Listen
,url.Values
,sync.WaitGroup
など)の典型的な使用シナリオを示しています。// Output:
コメントによる出力検証: 追加されたExample
関数の多くには、関数が標準出力に出力するであろう内容を正確に記述した// Output:
コメントが含まれています。go test
は、Example
関数を実行し、その標準出力とこのコメントの内容を比較することで、コード例が期待通りに動作するかを検証します。これにより、コード例が古くなったり、バグを含んだりするリスクが低減されます。- 既存のテストファイルへの追加と新規ファイルの作成:
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
関数を論理的に分離するための一般的なプラクティスです。
- パッケージ名の変更(一部):
md5_test.go
やsha1_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.go
の ExampleWriter
と ExampleReader
このファイルは、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.go
の ExampleNew
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.go
の ExampleNew
と ExampleNew_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 Blog: https://go.dev/blog/ (具体的な記事はコミット当時のものを見つけるのが難しいですが、
参考にした情報源リンク
- Go言語の公式ドキュメンテーション (pkg.go.dev)
- Go言語のGitHubリポジトリ (golang/go)
- コミットログと差分情報 (
/home/orange/Project/comemo/commit_data/12021.txt
)