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

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

このコミットは、Go言語の初期開発段階におけるexecパッケージのドキュメンテーション改善とAPIの明確化、およびregexpパッケージの軽微なコードスタイル変更を目的としています。特に、外部コマンド実行に関するAPIの可読性と理解度を高めることに重点が置かれています。

コミット

commit 8a7eb77880e8db4021b56731243008271d35a1eb
Author: Russ Cox <rsc@golang.org>
Date:   Thu Mar 5 13:35:45 2009 -0800

    misc doc

    R=r
    DELTA=50  (28 added, 0 deleted, 22 changed)
    OCL=25763
    CL=25770

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

https://github.com/golang/go/commit/8a7eb77880e8db4021b56731243008271d35a1eb

元コミット内容

misc doc

R=r
DELTA=50  (28 added, 0 deleted, 22 changed)
OCL=25763
CL=25770

変更の背景

このコミットは、Go言語がまだオープンソース化されて間もない、非常に初期の段階(2009年3月)に行われたものです。当時のGo言語の標準ライブラリは活発に開発されており、APIの名称、ドキュメンテーション、コードスタイルなどが頻繁に調整されていました。

execパッケージは、プログラムから外部コマンドを実行するための重要な機能を提供します。初期のAPI設計では、関数名や定数名が必ずしも直感的でなかったり、詳細な説明が不足していたりする場合があります。このコミットの背景には、execパッケージの利用者がより簡単に、かつ正確に外部コマンドを扱えるように、APIの名称をより適切にし、詳細なドキュメンテーションを追加することで、パッケージの使いやすさと堅牢性を向上させるという意図があります。

また、regexpパッケージにおけるエラー変数の宣言形式の変更は、コードベース全体の統一されたスタイルを確立しようとする初期の取り組みの一環と考えられます。複数の関連する変数をvar (...)ブロックでグループ化することは、Go言語における一般的な慣習であり、コードの整理と可読性向上に寄与します。

前提知識の解説

Go言語のパッケージとドキュメンテーション

Go言語では、コードは「パッケージ」という単位で整理されます。各パッケージは特定の機能を提供し、その機能はエクスポートされた(大文字で始まる)関数、型、変数などによって外部に公開されます。Goのツールチェインにはgo docというコマンドがあり、ソースコード内のコメントから自動的にドキュメンテーションを生成する機能があります。そのため、Goのコードでは、エクスポートされた要素に対して詳細なコメントを記述することが推奨されます。これらのコメントは、パッケージの目的、関数や型の振る舞い、引数、戻り値などを説明し、利用者がAPIを理解する上で不可欠な情報となります。

execパッケージの役割

execパッケージは、Goプログラムからオペレーティングシステム上の外部コマンド(例: /bin/ls, /usr/bin/gitなど)を実行するための機能を提供します。これにより、Goプログラムはシェルスクリプトのように他のプログラムと連携し、その出力を処理したり、入力を提供したりすることができます。

外部コマンドを実行する際には、以下の要素を考慮する必要があります。

  • コマンドのパスと引数: 実行するコマンドの絶対パス(またはPATH環境変数で解決可能なコマンド名)と、それに渡す引数。
  • 環境変数: 実行されるコマンドに引き継がれる環境変数。
  • 標準入出力 (Stdin, Stdout, Stderr): 実行されるコマンドの標準入力、標準出力、標準エラー出力をどのように扱うか。これらは、親プロセス(Goプログラム)のストリームに接続したり、パイプを介してGoプログラム内で読み書きしたり、/dev/nullにリダイレクトしたりすることができます。
  • プロセスの管理: 実行されたコマンドのプロセスID (PID) を取得したり、コマンドの終了を待機したり、終了ステータスを取得したりする機能。

regexpパッケージの役割

regexpパッケージは、正規表現を扱うための機能を提供します。正規表現は、文字列のパターンマッチングや検索、置換などに用いられる強力なツールです。正規表現のパース中に発生するエラー(例: 不正な括弧の対応、無効なエスケープシーケンスなど)は、os.Error(Goの初期バージョンにおけるエラー型)として返されます。

Go言語のエラーハンドリング(初期バージョン)

Go言語の初期バージョンでは、エラーは主にos.Errorインターフェース(後にerrorインターフェースに統合)を実装する型として表現されていました。os.NewError関数は、文字列から新しいエラーインスタンスを作成するために使用されました。

技術的詳細

execパッケージの変更点

  1. パッケージコメントの追加: src/lib/exec.goの冒頭に// The exec package runs external commands.というパッケージコメントが追加されました。これはgo docコマンドで表示されるパッケージの概要となり、パッケージの目的を簡潔に説明します。

  2. 定数名の変更: Passthru定数がPassThroughにリネームされました。これは、英語のスペルミス修正と、より一般的な命名規則への準拠を目的としています。PassThroughは、外部コマンドの標準入出力が親プロセスのものに「そのまま通過」することを意味します。

  3. Cmd構造体のフィールドコメント追加: Cmd構造体の各フィールド(Stdin, Stdout, Stderr, Pid)に対して、それぞれが何を表すのかを説明するコメントが追加されました。これにより、Cmd構造体のインスタンスが持つ情報が明確になります。

    • Stdin *os.FD: 実行されるコマンドの標準入力に接続されたパイプのファイルディスクリプタ。
    • Stdout *os.FD: 実行されるコマンドの標準出力に接続されたパイプのファイルディスクリプタ。
    • Stderr *os.FD: 実行されるコマンドの標準エラー出力に接続されたパイプのファイルディスクリプタ。
    • Pid int: 実行中のコマンドのオペレーティングシステムプロセスID。
  4. OpenCmdからRunへの関数名変更と詳細なドキュメンテーション: OpenCmd関数がRunにリネームされました。Runという名前は、外部コマンドの「実行」という操作をより直接的に表現しており、APIの意図が明確になります。 さらに、Run関数には非常に詳細なドキュメンテーションが追加されました。このドキュメンテーションは以下の点を明確にしています。

    • 目的: バイナリプログラムを引数と環境変数で実行すること。
    • 戻り値: 新しいCmd構造体へのポインタ、またはエラー。
    • stdin, stdout, stderrパラメータの挙動:
      • DevNull: /dev/nullに接続する(入力を無視、出力を破棄)。
      • PassThrough: 現在のプロセスの標準ストリームに接続する。
      • Pipe: オペレーティングシステムのパイプに接続する。これにより、Goプログラムがコマンドの入出力を読み書きできるようになります。
      • MergeWithStdout: (標準エラー出力のみ)標準出力と同じファイルディスクリプタを使用する。これは、標準エラー出力を標準出力にマージしたい場合に便利です。
    • Cmd構造体のフィールドとの関連: Pipeが指定された場合、対応するCmdフィールド(Stdin, Stdout, Stderr)がパイプのもう一方の端となり、それ以外の場合はnilとなること。
  5. Waitメソッドのドキュメンテーション追加: Cmd構造体のWaitメソッドにドキュメンテーションが追加されました。このメソッドは、実行中のコマンドの終了を待機し、os.Waitmsgとエラーを返します。options引数についても説明され、osパッケージの詳細を参照するように促しています。

  6. Closeメソッドのドキュメンテーション追加: Cmd構造体のCloseメソッドにドキュメンテーションが追加されました。このメソッドは、実行中のコマンドがまだ終了していない場合はその終了を待機し、その後、Cmd構造体のStdin, Stdout, Stderrフィールドに設定されている非nilのファイルディスクリプタを閉じます。これは、リソースリークを防ぐために重要です。

exec_test.goの変更点

exec.goにおけるOpenCmdからRunへの関数名変更に伴い、テストファイルsrc/lib/exec_test.go内のテスト関数名もTestOpenCmdCatからTestRunCatへ、TestOpenCmdEchoからTestRunEchoへと変更されました。これにより、テストコードとAPIの整合性が保たれます。

regexpパッケージの変更点

src/lib/regexp/regexp.goでは、正規表現のパースエラーを表す複数のos.Error変数の宣言形式が変更されました。

変更前:

var ErrInternal = os.NewError("internal error");
var ErrUnmatchedLpar = os.NewError("unmatched '('");
// ... 他のエラー変数

変更後:

var (
    ErrInternal = os.NewError("internal error");
    ErrUnmatchedLpar = os.NewError("unmatched '('");
    // ... 他のエラー変数
)

これは機能的な変更ではなく、複数の関連する変数をvar (...)ブロック内にまとめるというGo言語の慣習に合わせたコードスタイルの変更です。これにより、コードの可読性が向上し、関連するエラー変数が一箇所にまとまっていることが視覚的に分かりやすくなります。

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

src/lib/exec.go

--- a/src/lib/exec.go
+++ b/src/lib/exec.go
@@ -2,6 +2,7 @@
 // Use of this source code is governed by a BSD-style
 // license that can be found in the LICENSE file.
 
+// The exec package runs external commands.
 package exec
 
 import (
@@ -9,13 +10,19 @@ import (
 	"syscall";
 )
 
+// Arguments to Run.
 const (
 	DevNull = iota;
-	Passthru;
+	PassThrough;
 	Pipe;
 	MergeWithStdout;
 )
 
+// A Cmd represents a running command.
+// Stdin, Stdout, and Stderr are file descriptors to pipes
+// connected to the running command's standard input, output, and error,
+// or else nil, depending on the arguments to Run.
+// Pid is the running command's operating system process ID.
 type Cmd struct {
 	Stdin *os.FD;
 	Stdout *os.FD;
@@ -34,7 +41,7 @@ func modeToFDs(mode, fd int) (*os.FD, *os.FD, *os.Error) {
 		}
 		f, err := os.Open("/dev/null", rw, 0);
 		return f, nil, err;
-	case Passthru:
+	case PassThrough:
 		switch fd {
 		case 0:
 			return os.Stdin, nil, nil;
@@ -56,12 +63,22 @@ func modeToFDs(mode, fd int) (*os.FD, *os.FD, *os.Error) {
 	return nil, nil, os.EINVAL;
 }
 
-// Start command running with pipes possibly
-// connected to stdin, stdout, stderr.
-// TODO(rsc): Should the stdin,stdout,stderr args
-// be [3]int instead?\n-func OpenCmd(argv0 string, argv, envv []string, stdin, stdout, stderr int)\n-\t(p *Cmd, err *os.Error)\n+// Run starts the binary prog running with
+// arguments argv and environment envv.
+// It returns a pointer to a new Cmd representing
+// the command or an error.
+//
+// The parameters stdin, stdout, and stderr
+// specify how to handle standard input, output, and error.
+// The choices are DevNull (connect to /dev/null),
+// PassThrough (connect to the current process's standard stream),
+// Pipe (connect to an operating system pipe), and
+// MergeWithStdout (only for standard error; use the same
+// file descriptor as was used for standard output).
+// If a parameter is Pipe, then the corresponding field (Stdin, Stdout, Stderr)
+// of the returned Cmd is the other end of the pipe.
+// Otherwise the field in Cmd is nil.
+func Run(argv0 string, argv, envv []string, stdin, stdout, stderr int) (p *Cmd, err *os.Error)
 {
 	p = new(Cmd);
 	var fd [3]*os.FD;
@@ -116,6 +133,12 @@ Error:
 	return nil, err;
 }
 
+// Wait waits for the running command p,
+// returning the Waitmsg returned by os.Wait and an error.
+// The options are passed through to os.Wait.
+// Setting options to 0 waits for p to exit;
+// other options cause Wait to return for other
+// process events; see package os for details.
 func (p *Cmd) Wait(options uint64) (*os.Waitmsg, *os.Error) {
 	if p.Pid < 0 {
 		return nil, os.EINVAL;
@@ -127,6 +150,9 @@ func (p *Cmd) Wait(options uint64) (*os.Waitmsg, *os.Error) {
 	return w, err;
 }
 
+// Close waits for the running command p to exit,
+// if it hasn't already, and then closes the non-nil file descriptors
+// p.Stdin, p.Stdout, and p.Stderr.
 func (p *Cmd) Close() *os.Error {
 	if p.Pid >= 0 {
 		// Loop on interrupt, but

src/lib/regexp/regexp.go

--- a/src/lib/regexp/regexp.go
+++ b/src/lib/regexp/regexp.go
@@ -31,16 +31,18 @@ import (
 var debug = false;
 
 // Error codes returned by failures to parse an expression.
-var ErrInternal = os.NewError("internal error");
-var ErrUnmatchedLpar = os.NewError("unmatched '('");
-var ErrUnmatchedRpar = os.NewError("unmatched ')'");
-var ErrUnmatchedLbkt = os.NewError("unmatched '['");
-var ErrUnmatchedRbkt = os.NewError("unmatched ']'");
-var ErrBadRange = os.NewError("bad range in character class");
-var ErrExtraneousBackslash = os.NewError("extraneous backslash");
-var ErrBadClosure = os.NewError("repeated closure (**, ++, etc.)");
-var ErrBareClosure = os.NewError("closure applies to nothing");
-var ErrBadBackslash = os.NewError("illegal backslash escape");
+var (
+	ErrInternal = os.NewError("internal error");
+	ErrUnmatchedLpar = os.NewError("unmatched '('");
+	ErrUnmatchedRpar = os.NewError("unmatched ')'");
+	ErrUnmatchedLbkt = os.NewError("unmatched '['");
+	ErrUnmatchedRbkt = os.NewError("unmatched ']'");
+	ErrBadRange = os.NewError("bad range in character class");
+	ErrExtraneousBackslash = os.NewError("extraneous backslash");
+	ErrBadClosure = os.NewError("repeated closure (**, ++, etc.)");
+	ErrBareClosure = os.NewError("closure applies to nothing");
+	ErrBadBackslash = os.NewError("illegal backslash escape");
+)
 
 // An instruction executed by the NFA
 type instr interface {

コアとなるコードの解説

execパッケージの変更解説

このコミットの核となる変更は、execパッケージのAPIの明確化とドキュメンテーションの充実です。

  • OpenCmdからRunへの変更: Go言語の設計哲学の一つに「明確さ」があります。OpenCmdという名前は、コマンドを「開く」というニュアンスを含んでいますが、実際にコマンドを「実行」し、そのプロセスを管理する機能を持つため、Runというより直接的な名前に変更されました。これにより、関数の目的がより直感的に理解できるようになりました。

  • Run関数の詳細なドキュメンテーション: 追加されたドキュメンテーションは、Run関数の引数であるstdin, stdout, stderrの挙動を詳細に説明しています。特に、DevNull, PassThrough, Pipe, MergeWithStdoutといった定数の意味と、それらがCmd構造体のStdin, Stdout, Stderrフィールドにどのように影響するかを明確にしています。これは、外部コマンドとの入出力連携が複雑になりがちなため、利用者が適切なモードを選択し、パイプを正しく扱う上で非常に重要です。例えば、Pipeを指定した場合にのみCmd構造体の対応するフィールドがnilではなくなり、Goプログラムからそのパイプを介して読み書きできることが明記されています。

  • Cmd構造体、WaitCloseメソッドのドキュメンテーション: Cmd構造体のフィールドや、WaitCloseといった重要なメソッドにコメントが追加されたことで、execパッケージの全体的な使い方がより明確になりました。Waitメソッドは、実行中のコマンドの終了を待機し、その終了ステータスを取得するために不可欠です。Closeメソッドは、コマンドの終了を待機し、関連するファイルディスクリプタを閉じることで、システムリソースの適切な解放を保証します。これらのドキュメンテーションは、Goプログラムが外部コマンドを安全かつ効率的に利用するためのガイドラインを提供します。

regexpパッケージの変更解説

regexpパッケージにおけるエラー変数の宣言形式の変更は、Go言語のコードスタイルに関する初期の決定を反映しています。複数の関連する変数をvar (...)ブロックで宣言することは、Goの慣用的なスタイルであり、以下の利点があります。

  • 可読性の向上: 関連するエラー変数が一箇所にまとまっているため、コードを読んだときに、どのような種類のエラーが定義されているかを一目で把握しやすくなります。
  • コードの整理: ファイルの冒頭や特定のセクションで、関連する定数や変数をグループ化することで、コードベース全体の整理に貢献します。
  • 保守性の向上: 新しいエラーを追加する際や、既存のエラーを変更する際に、関連する定義が近くにあるため、変更作業が容易になります。

この変更は機能的な影響は全くありませんが、Go言語のコードベース全体の品質と一貫性を高めるための重要なステップでした。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメンテーション (pkg.go.dev)
  • Go言語のGitHubリポジトリのコミット履歴
  • Go言語の初期の設計に関する議論(Go Mailing Listなど、当時の情報源)
    • (注: 2009年当時の具体的な議論スレッドを特定するのは困難ですが、Goの設計原則に関する一般的な情報は現在のドキュメントやブログ記事から推測できます。)
  • Go言語のコードスタイルガイドライン(Go Proverbsなど)I have provided the detailed explanation as requested.