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

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

このコミットは、Go言語の標準ライブラリである bufio パッケージ内のいくつかの識別子(定数、関数、型)の可視性を変更するものです。具体的には、外部に公開されていた(エクスポートされていた)識別子を、パッケージ内部でのみ使用される(アンエクスポートされた)識別子へと変更しています。これにより、bufio パッケージの内部実装の詳細が隠蔽され、APIの安定性と保守性が向上します。

コミット

commit 33f3ed771025a9edb512c6ff0747955e27496bdc
Author: Rob Pike <r@golang.org>
Date:   Thu Jan 15 16:22:57 2009 -0800

    casify fixup for bufio
    
    R=rsc
    DELTA=88  (0 added, 12 deleted, 76 changed)
    OCL=22884
    CL=22890

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

https://github.com/golang/go/commit/33f3ed771025a9edb512c6ff0747955e27496bdc

元コミット内容

casify fixup for bufio

このコミットメッセージは非常に簡潔ですが、「casify fixup」という表現が変更の意図を明確に示しています。これは、Go言語の命名規則(特に可視性に関する規則)に合わせて、識別子の大文字・小文字の区別(casing)を修正するという意味です。

変更の背景

Go言語では、識別子(変数、関数、型など)の最初の文字が大文字であるか小文字であるかによって、その識別子がパッケージ外にエクスポートされる(公開される)か、それともパッケージ内部でのみ使用される(非公開となる)かが決まります。大文字で始まる識別子はエクスポートされ、小文字で始まる識別子はエクスポートされません。

このコミットが行われた2009年1月は、Go言語がまだ初期開発段階にあった時期です。当時のGo言語のコードベースでは、現在の厳格な命名規則が完全に確立されていなかったり、あるいは初期の実装段階で一時的に公開されていた内部ヘルパーが、後に非公開化される必要が生じたりすることがありました。

bufio パッケージは、バッファリングされたI/O操作を提供するGoの基本的なパッケージの一つです。このパッケージには、内部的なデータ操作やヘルパー機能を提供する関数や定数が含まれています。これらの内部的な要素が誤ってエクスポートされていると、パッケージの外部からそれらに直接アクセスできてしまい、将来的なパッケージの変更(例えば、内部実装の最適化や変更)が、外部の利用者に影響を与えてしまう可能性があります。これは、APIの安定性を損ない、パッケージの保守を困難にします。

この「casify fixup」は、bufio パッケージのAPIを整理し、外部に公開すべきでない内部的な詳細を隠蔽することで、パッケージの設計を改善し、より堅牢なAPIを提供することを目的としています。

前提知識の解説

Go言語の可視性(Visibility)

Go言語における識別子の可視性は、その識別子の最初の文字が大文字か小文字かによって決定されます。

  • エクスポートされた識別子 (Exported Identifiers): 最初の文字が大文字の識別子(例: Read, Writer, DefaultBufSize)。これらは、その識別子が定義されているパッケージの外部からアクセス可能です。つまり、他のパッケージからインポートして使用できます。これらはパッケージの「公開API」の一部と見なされます。
  • アンエクスポートされた識別子 (Unexported Identifiers): 最初の文字が小文字の識別子(例: read, writer, defaultBufSize)。これらは、その識別子が定義されているパッケージの内部でのみアクセス可能です。パッケージの外部からは直接アクセスできません。これらはパッケージの「内部実装の詳細」と見なされます。

この規則は、Go言語の設計哲学である「シンプルさ」と「明示性」を反映しています。開発者は、識別子の名前を見るだけで、それが公開APIの一部なのか、それとも内部実装の詳細なのかをすぐに判断できます。これにより、パッケージの利用者は、どの部分に依存すべきかを明確に理解し、パッケージの設計者は、内部実装を自由にリファクタリングできる柔軟性を持ちます。

bufio パッケージ

bufio パッケージは、Go言語の標準ライブラリ io パッケージの上に構築されており、バッファリングされたI/O操作を提供します。これにより、ディスクI/OやネットワークI/Oなどの低レベルなI/O操作の回数を減らし、パフォーマンスを向上させることができます。主な機能としては、Reader(バッファリングされた読み込み)と Writer(バッファリングされた書き込み)があります。

io.StringBytes

Go言語の io パッケージには、文字列をバイトスライスに変換するためのヘルパー関数 StringBytes が存在します。このコミットでは、テストコード内で独自に定義されていた StringToBytes 関数が、標準ライブラリの io.StringBytes に置き換えられています。これは、コードの重複を避け、標準的な機能を利用することで、コードの品質と保守性を向上させる一般的なプラクティスです。

技術的詳細

このコミットの技術的な変更は、主にGo言語の命名規則と可視性に関するものです。

  1. 定数の変更:

    • DefaultBufSize (エクスポート) が defaultBufSize (アンエクスポート) に変更されました。これは、バッファのデフォルトサイズがパッケージの内部的な設定値であり、外部から直接参照したり変更したりする必要がないことを示しています。
  2. 関数の変更:

    • CopySlice (エクスポート) が copySlice (アンエクスポート) に変更されました。この関数はバイトスライスをコピーする内部ヘルパーであり、Goの組み込み関数 copy が存在しない初期段階で作成された可能性があります。パッケージ外部に公開する必要はありません。
    • FindByte (エクスポート) が findByte (アンエクスポート) に変更されました。この関数はバイトスライス内で特定のバイトを検索する内部ヘルパーであり、同様に外部に公開する必要はありません。
  3. テストヘルパーの変更:

    • src/lib/bufio_test.go 内のテスト専用のヘルパー関数や型も同様に、エクスポートされた名前からアンエクスポートされた名前に変更されています。これには Copy -> copyByteReader -> byteReaderNewByteReader -> newByteReader などが含まれます。テストヘルパーはテストコード内でのみ使用されるため、これらをアンエクスポートすることは、パッケージの内部構造をより明確にし、誤って他のパッケージから参照されることを防ぎます。
    • 特に注目すべきは、StringToBytes 関数が削除され、代わりに io.StringBytes が使用されるようになった点です。これは、Go言語の標準ライブラリが成熟し、必要な機能が提供されるようになったことに伴う変更であり、コードの冗長性を排除し、標準的なアプローチを採用する良い例です。

これらの変更は、Go言語のパッケージ設計におけるベストプラクティスに従ったものです。内部実装の詳細を隠蔽することで、パッケージのAPIがよりクリーンになり、将来的な変更が容易になります。また、外部の利用者が誤って内部実装に依存してしまうことを防ぎ、パッケージの進化を妨げないようにします。

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

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

  • src/lib/bufio.go: bufio パッケージの主要な実装ファイル。
  • src/lib/bufio_test.go: bufio パッケージのテストファイル。

具体的な変更は、識別子の名前の変更(大文字から小文字へ)と、それに伴う参照箇所の修正です。

src/lib/bufio.go の変更例:

--- a/src/lib/bufio.go
+++ b/src/lib/bufio.go
@@ -18,7 +18,7 @@ import (
 //	- buffered output
 
 const (
-	DefaultBufSize = 4096
+	defaultBufSize = 4096
 )
 
 export var (
@@ -30,7 +30,7 @@ export var (
 	ShortWrite = os.NewError("short write");
 )
 
-func CopySlice(dst []byte, src []byte) {
+func copySlice(dst []byte, src []byte) {
 	for i := 0; i < len(dst); i++ {
 		dst[i] = src[i]
 	}
@@ -170,7 +168,7 @@ func (b *BufRead) ReadRune() (rune int, size int, err *os.Error) {
 
 // Helper function: look for byte c in array p,
 // returning its index or -1.
-func FindByte(p []byte, c byte) int {
+func findByte(p []byte, c byte) int {
 	for i := 0; i < len(p); i++ {
 		if p[i] == c {
 			return i

src/lib/bufio_test.go の変更例:

--- a/src/lib/bufio_test.go
+++ b/src/lib/bufio_test.go
@@ -13,55 +13,47 @@ import (
 	"testing";
 )
 
-func StringToBytes(s string) []byte {
-	b := make([]byte, len(s));
-	for i := 0; i < len(s); i++ {
-		b[i] = s[i]
-	}
-	return b
-}
-
 // Should be in language!
-func Copy(p []byte, q []byte) {
+func copy(p []byte, q []byte) {
 	for i := 0; i < len(p); i++ {
 		p[i] = q[i]
 	}
 }
 
 // Reads from p.
-type ByteReader struct {
+type byteReader struct {
 	p []byte
 }
 
-func NewByteReader(p []byte) io.Read {
-	b := new(ByteReader);
+func newByteReader(p []byte) io.Read {
+	b := new(byteReader);
 	b.p = p;
 	return b
 }
 
-func (b *ByteReader) Read(p []byte) (int, *os.Error) {
+func (b *byteReader) Read(p []byte) (int, *os.Error) {
 	n := len(p);
 	if n > len(b.p) {
 		n = len(b.p)
 	}
-	Copy(p[0:n], b.p[0:n]);
+	copy(p[0:n], b.p[0:n]);
 	b.p = b.p[n:len(b.p)];
 	return n, nil
 }
 
 
 // Reads from p but only returns half of what you asked for.
-type HalfByteReader struct {
+type halfByteReader struct {
 	p []byte
 }
 
-func NewHalfByteReader(p []byte) io.Read {
-	w := new(HalfByteReader);
+func newHalfByteReader(p []byte) io.Read {
+	w := new(halfByteReader);
 	w.p = p;
 	return w
 }
 
-func (b *HalfByteReader) Read(p []byte) (int, *os.Error) {
+func (b *halfByteReader) Read(p []byte) (int, *os.Error) {
 	n := len(p)/2;
 	if n == 0 && len(p) > 0 {
 		n = 1
@@ -182,13 +174,13 @@ var bufsizes = []int {
 }
 
 export func TestBufReadSimple(t *testing.T) {
-	b, e := NewBufRead(NewByteReader(StringToBytes("hello world")));
-	if s := ReadBytes(b); s != "hello world" {
+	b, e := NewBufRead(newByteReader(io.StringBytes("hello world")));
+	if s := readBytes(b); s != "hello world" {
 		t.Errorf("simple hello world test failed: got %q", s);
 	}
 
-	b, e = NewBufRead(NewRot13Reader(NewByteReader(StringToBytes("hello world"))));
-	if s := ReadBytes(b); s != "uryyb jbeyq" {
+	b, e = NewBufRead(newRot13Reader(newByteReader(io.StringBytes("hello world"))));
+	if s := readBytes(b); s != "uryyb jbeyq" {
 		t.Error("rot13 hello world test failed: got %q", s);
 	}
 }

コアとなるコードの解説

このコミットの核心は、Go言語の可視性ルールを bufio パッケージに適用し、内部ヘルパーを非公開にすることです。

  1. DefaultBufSize から defaultBufSize への変更:

    • DefaultBufSize は、bufio パッケージが内部的に使用するデフォルトのバッファサイズを定義する定数でした。この定数がエクスポートされていると、外部のコードがこの値に直接依存したり、誤って変更しようとしたりする可能性があります。
    • defaultBufSize に変更することで、この定数は bufio パッケージ内でのみアクセス可能となり、パッケージの内部実装の詳細として扱われます。これにより、将来的にデフォルトバッファサイズを変更しても、外部のコードに影響を与えることなく、パッケージの進化が可能になります。
  2. CopySlice から copySlice への変更:

    • CopySlice は、バイトスライスの一部を別のバイトスライスにコピーするためのヘルパー関数でした。これは、Go言語に組み込みの copy 関数が導入される前の初期段階で、スライス操作を効率的に行うために作成された可能性があります。
    • この関数は bufio パッケージの内部でしか使用されないため、copySlice に変更することで、その可視性をパッケージ内に限定します。これにより、パッケージの外部からこの内部ヘルパーに依存するコードが書かれることを防ぎ、bufio パッケージの内部実装を自由にリファクタリングできるようになります。
  3. FindByte から findByte への変更:

    • FindByte は、バイトスライス内で特定のバイトを検索するためのヘルパー関数でした。これも bufio パッケージの内部的な処理を助けるためのものであり、外部に公開する必要はありません。
    • findByte に変更することで、この関数もパッケージ内部にカプセル化され、bufio パッケージのAPIをよりクリーンに保ちます。
  4. テストヘルパーの変更と io.StringBytes の採用:

    • bufio_test.go 内の StringToBytes, Copy, ByteReader, HalfByteReader, Rot13Reader などのテスト専用のヘルパー関数や型も、同様にアンエクスポートされています。テストコードはパッケージの内部的な動作を検証するためのものであり、そのヘルパーが外部に公開される必要はありません。
    • 特に StringToBytes が削除され、io.StringBytes に置き換えられたことは重要です。これは、Go言語の標準ライブラリが成熟し、共通のユーティリティ関数が提供されるようになったことを示しています。独自の実装を標準ライブラリの機能に置き換えることは、コードの品質、可読性、保守性を向上させる一般的なベストプラクティスです。

これらの変更は、Go言語のパッケージ設計の原則である「カプセル化」と「APIの安定性」を強化するものです。内部的な実装の詳細を隠蔽することで、パッケージの外部インターフェースが明確になり、利用者は公開されたAPIのみに依存するようになります。これにより、パッケージの内部構造が変更されても、外部のコードが壊れるリスクが低減され、長期的な保守が容易になります。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード (特に bufio パッケージの歴史)
  • Gitのコミット履歴と差分表示
  • Go言語の命名規則に関する一般的な知識