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

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

このコミットは、Go言語の初期段階におけるいくつかの小さなクリーンアップと改善を目的としています。具体的には、ドキュメントの修正、テストコードにおけるリソース解放の改善(deferの使用)、そして正規表現エンジンの内部構造体名の変更(プライベートな命名規則への準拠)が含まれています。全体として、コードベースの整合性と堅牢性を高めるための調整が行われています。

コミット

commit 6506148850a8404979df82b4f1ca7620cd8e9c16
Author: Rob Pike <r@golang.org>
Date:   Sun Feb 8 10:17:23 2009 -0800

    a few small cleanups
    
    R=rsc
    DELTA=21  (2 added, 2 deleted, 17 changed)
    OCL=24638
    CL=24654

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

https://github.com/golang/go/commit/6506148850a8404979df82b4f1ca7620cd8e9c16

元コミット内容

a few small cleanups

R=rsc
DELTA=21  (2 added, 2 deleted, 17 changed)
OCL=24638
CL=24654

変更の背景

このコミットは、Go言語がまだ活発に開発されていた初期の段階で行われたものです。変更の背景には、以下の点が考えられます。

  1. Go言語の命名規則の確立: Go言語では、パッケージ外からアクセスできない(プライベートな)識別子には小文字で始まる名前を使用するという慣習があります。src/lib/regexp/regexp.goにおける_Commonからcommonへの変更は、この命名規則への準拠を徹底するためのものです。_プレフィックスは、C言語など他の言語での慣習に由来する可能性があり、Goの慣習に合わせて修正されたと考えられます。
  2. リソース管理のベストプラクティスの導入: src/lib/log_test.goにおけるdeferキーワードの導入は、リソース(この場合はパイプのファイルディスクリプタ)の解放を確実に行うためのものです。deferは、関数がリターンする直前に指定された関数を実行するため、エラーパスや複数のリターンポイントがある場合でも、リソースのクリーンアップを忘れずに行うための堅牢なメカニズムを提供します。これは、Go言語の設計思想である「シンプルさと堅牢性」に合致する変更です。
  3. ドキュメントの正確性の向上: doc/go_tutorial.txtの変更は、チュートリアル内のコードスニペットがGo言語の実際の仕様と一致するように修正されたものです。export type Write interfaceからtype Write interfaceへの変更は、インターフェースがパッケージ外にエクスポートされるかどうかを示すexportキーワードが不要になったか、あるいは初期のチュートリアルで誤って記述されていたものを修正した可能性があります。Go言語では、識別子が大文字で始まる場合にのみエクスポートされるため、exportキーワードは冗長です。

これらの変更は、Go言語の設計原則に基づき、コードの可読性、保守性、堅牢性を向上させるための継続的な取り組みの一環として行われました。

前提知識の解説

このコミットを理解するためには、以下のGo言語の基本的な概念を理解しておく必要があります。

  • Go言語の命名規則: Go言語では、識別子(変数、関数、型など)の最初の文字が大文字である場合、その識別子はパッケージ外にエクスポートされ、他のパッケージからアクセス可能になります。小文字で始まる場合、その識別子はプライベートであり、定義されたパッケージ内でのみアクセス可能です。このコミットでは、正規表現パッケージ内の内部構造体_Commoncommonにリネームされており、これはプライベートな識別子であることを明確にするための変更です。
  • deferステートメント: deferステートメントは、そのステートメントを含む関数がリターンする直前に、指定された関数呼び出しを実行するようにスケジュールします。これは、ファイルやネットワーク接続などのリソースを確実にクリーンアップするために非常によく使用されます。deferはLIFO(Last-In, First-Out)順で実行されるため、複数のdeferステートメントがある場合、最後にスケジュールされたものが最初に実行されます。このコミットでは、テストコード内でパイプのファイルディスクリプタを閉じるためにdeferが導入されています。
  • インターフェース: Go言語のインターフェースは、メソッドのシグネチャの集合を定義する型です。特定のインターフェースのすべてのメソッドを実装する任意の型は、そのインターフェースを満たします。Goのインターフェースは「暗黙的」であり、型がインターフェースを実装していることを明示的に宣言する必要はありません。doc/go_tutorial.txtの変更は、io.Writeインターフェースの定義に関するもので、exportキーワードが削除されています。これは、Goの命名規則により、Writeが大文字で始まるため自動的にエクスポートされるため、exportキーワードが冗長であるためです。
  • 正規表現エンジン (regexpパッケージ): Go言語の標準ライブラリには、正規表現を扱うためのregexpパッケージが含まれています。このパッケージは、正規表現のコンパイル、マッチング、置換などの機能を提供します。このコミットでは、regexpパッケージの内部実装に関する変更が含まれており、正規表現の命令(instr)を表現するための共通構造体_Commoncommonにリネームされています。

技術的詳細

このコミットは、3つの異なるファイルに対する変更を含んでいます。

  1. doc/go_tutorial.txt:

    • 変更前: export type Write interface {
    • 変更後: type Write interface {
    • この変更は、Go言語の命名規則に基づいています。Goでは、識別子(この場合はWriteインターフェース)の最初の文字が大文字であれば、自動的にパッケージ外にエクスポートされます。したがって、exportキーワードは冗長であり、削除されました。これは、チュートリアルがGo言語の正確な構文と慣習を反映するようにするための修正です。
  2. src/lib/log_test.go:

    • 変更前は、fd0.Close()fd1.Close()が関数の最後に明示的に呼び出されていました。
    • 変更後: defer fd0.Close();defer fd1.Close(); が追加され、元のClose()呼び出しは削除されました。
    • この変更により、testLog関数がどのように終了しても(正常終了、エラーによる早期リターンなど)、fd0fd1が確実に閉じられるようになりました。deferステートメントは、関数がリターンする直前に実行されるため、リソースリークを防ぐための堅牢な方法を提供します。これは、テストコードの信頼性を向上させるための重要な改善です。
  3. src/lib/regexp/regexp.go:

    • このファイルでは、正規表現の内部表現で使用される複数の構造体(_Common, _Start, _End, _Bot, _Eot, _Char, _CharClass, _Any, _Bra, _Ebra, _Alt, _Nop)が変更されています。
    • 具体的には、_Commonという名前の構造体がcommonにリネームされ、それに伴い、この構造体を埋め込んでいる他のすべての構造体(例: _Start_Commonを埋め込んでいた箇所)もcommonを埋め込むように変更されました。
    • また、_Common構造体のメソッド(Next, SetNext, Index, SetIndex)のレシーバも*_Commonから*commonに変更されました。
    • この変更は、Go言語の命名規則に厳密に準拠するためのものです。_プレフィックスは、C言語などでは内部的な使用を示す慣習として使われることがありますが、Go言語では小文字で始まる識別子がプライベートであることを示します。したがって、_Commoncommonにリネームすることで、この構造体がパッケージ内部でのみ使用されることをより明確に示し、Goの慣習に合わせました。

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

doc/go_tutorial.txt

--- a/doc/go_tutorial.txt
+++ b/doc/go_tutorial.txt
@@ -661,7 +661,7 @@ etc., there are also "Fprintf" etc.  Unlike in C, "Fprintf"'s first argument is
 not a file.  Instead, it is a variable of type "io.Write", which is an
 interface type defined in the "io" library:
 
-	export type Write interface {
+	type Write interface {
 		Write(p []byte) (n int, err *os.Error);
 	}

src/lib/log_test.go

--- a/src/lib/log_test.go
+++ b/src/lib/log_test.go
@@ -51,6 +51,8 @@ func testLog(t *testing.T, flag int, prefix string, pattern string, useLogf bool
 	if err1 != nil {
 		t.Fatal("pipe", err1);
 	}
+	defer fd0.Close();
+	defer fd1.Close();
 	buf := bufio.NewBufRead(fd0);
 	l := NewLogger(fd1, nil, prefix, flag);
 	if useLogf {
@@ -70,8 +72,6 @@ func testLog(t *testing.T, flag int, prefix string, pattern string, useLogf bool
 	if !matched {
 		t.Errorf("log output should match %q is %q", pattern, line);
 	}
-	fd0.Close();
-	fd1.Close();
 }

src/lib/regexp/regexp.go

--- a/src/lib/regexp/regexp.go
+++ b/src/lib/regexp/regexp.go
@@ -37,15 +37,15 @@ type instr interface {
 }
 
 // Fields and methods common to all instructions
-type _Common struct {
+type common struct {
 	next	instr;
 	index	int;
 }
 
-func (c *_Common) Next() instr { return c.next }
-func (c *_Common) SetNext(i instr) { c.next = i }
-func (c *_Common) Index() int { return c.index }
-func (c *_Common) SetIndex(i int) { c.index = i }
+func (c *common) Next() instr { return c.next }
+func (c *common) SetNext(i instr) { c.next = i }
+func (c *common) Index() int { return c.index }
+func (c *common) SetIndex(i int) { c.index = i }
 
 type _RE struct {
 	expr	string;	// the original expression
@@ -73,7 +73,7 @@ const (
 
 // --- START start of program
 type _Start struct {
-	_Common
+	common
 }
 
 func (start *_Start) Type() int { return _START }
@@ -81,7 +81,7 @@ func (start *_Start) Print() { print("start") }
 
 // --- END end of program
 type _End struct {
-	_Common
+	common
 }
 
 func (end *_End) Type() int { return _END }
@@ -89,7 +89,7 @@ func (end *_End) Print() { print("end") }
 
 // --- BOT beginning of text
 type _Bot struct {
-	_Common
+	common
 }
 
 func (bot *_Bot) Type() int { return _BOT }
@@ -97,7 +97,7 @@ func (bot *_Bot) Print() { print("bot") }
 
 // --- EOT end of text
 type _Eot struct {
-	_Common
+	common
 }
 
 func (eot *_Eot) Type() int { return _EOT }
@@ -105,7 +105,7 @@ func (eot *_Eot) Print() { print("eot") }
 
 // --- CHAR a regular character
 type _Char struct {
-	_Common;
+	common;
 	char	int;
 }
 
@@ -121,7 +121,7 @@ func newChar(char int) *_Char {
 // --- CHARCLASS [a-z]
 
 type _CharClass struct {
-	_Common;
+	common;
 	char	int;
 	negate	bool;	// is character class negated? ([^a-z])
 	// array of int, stored pairwise: [a-z] is (a,z); x is (x,x):
@@ -171,7 +171,7 @@ func newCharClass() *_CharClass {
 
 // --- ANY any character
 type _Any struct {
-	_Common
+	common
 }
 
 func (any *_Any) Type() int { return _ANY }
@@ -179,7 +179,7 @@ func (any *_Any) Print() { print("any") }
 
 // --- BRA parenthesized expression
 type _Bra struct {
-	_Common;
+	common;
 	n	int;	// subexpression number
 }
 
@@ -188,7 +188,7 @@ func (bra *_Bra) Print() { print("bra", bra.n); }
 
 // --- EBRA end of parenthesized expression
 type _Ebra struct {
-	_Common;
+	common;
 	n	int;	// subexpression number
 }
 
@@ -197,7 +197,7 @@ func (ebra *_Ebra) Print() { print("ebra ", ebra.n); }
 
 // --- ALT alternation
 type _Alt struct {
-	_Common;
+	common;
 	left	instr;	// other branch
 }
 
@@ -206,7 +206,7 @@ func (alt *_Alt) Print() { print("alt(", alt.left.Index(), ")"); }
 
 // --- NOP no operation
 type _Nop struct {
-	_Common
+	common
 }
 
 func (nop *_Nop) Type() int { return _NOP }

コアとなるコードの解説

doc/go_tutorial.txtの変更

この変更は、Go言語のチュートリアルにおけるio.Writeインターフェースの定義からexportキーワードを削除したものです。Go言語では、識別子(変数名、関数名、型名など)の最初の文字が大文字であれば、その識別子は自動的にパッケージ外にエクスポートされます。したがって、WriteインターフェースはWが大文字であるため、exportキーワードがなくてもエクスポートされます。この修正は、チュートリアルの内容をGo言語の実際の仕様と慣習に合わせるためのものであり、冗長なキーワードを削除することでコードの簡潔性を保つというGoの哲学にも合致しています。

src/lib/log_test.goの変更

この変更は、テスト関数testLog内で作成されたパイプのファイルディスクリプタ(fd0fd1)のクリーンアップ方法を改善したものです。変更前は、関数の最後に明示的にfd0.Close()fd1.Close()が呼び出されていました。しかし、関数が途中でエラーを返したり、複数のリターンポイントがあったりする場合、これらのClose()呼び出しがスキップされ、リソースリークが発生する可能性がありました。

deferステートメントを導入することで、fd0.Close()fd1.Close()testLog関数が終了する直前に必ず実行されるようになります。これにより、関数の実行パスに関わらず、リソースが確実に解放されることが保証され、テストの堅牢性が向上します。これは、Go言語におけるリソース管理のベストプラクティスの一つです。

src/lib/regexp/regexp.goの変更

このファイルにおける最も重要な変更は、正規表現エンジンの内部で使用される共通構造体_Commoncommonにリネームされたことです。これに伴い、_Commonを埋め込んでいる他のすべての正規表現命令構造体(例: _Start, _End, _Charなど)も、埋め込む構造体の名前をcommonに変更しています。また、_Commonのメソッドのレシーバも*_Commonから*commonに変更されました。

この変更の目的は、Go言語の命名規則に厳密に準拠することです。Goでは、小文字で始まる識別子はパッケージプライベートであり、パッケージ外からはアクセスできません。_プレフィックスは、C言語などの他のプログラミング言語で内部的な使用を示すために使われることがありますが、Go言語では単に小文字で始まることでプライベートであることが示されます。したがって、_Commoncommonにリネームすることで、この構造体が正規表現パッケージの内部実装の詳細であり、外部から直接アクセスされるべきではないことをより明確に示しています。これは、コードの可読性とGo言語の慣習への準拠を向上させるためのクリーンアップです。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のEffective Go
  • GitHubのgolang/goリポジトリのコミット履歴
  • Go言語に関する一般的なプログラミング知識