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

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

このコミットは、Go言語の初期開発段階における重要な変更を示しています。主に、内部的な sys パッケージの関数名がGo言語の慣習に沿った camelCase に変更され、export キーワードの扱いが調整されています。また、コマンドライン引数の取得方法が sys.argc()sys.argv() といったC言語ライクな関数から、よりGoらしい sys.Args スライスへと移行されました。さらに、文字列とルーン(Unicodeコードポイント)間の変換に sys.stringtorune ではなく、標準ライブラリの utf8.DecodeRuneInString が使用されるように変更され、src/runtime/rune.c および src/runtime/sys_file.c の一部機能が削除または再編成されています。これらの変更は、Go言語のAPI設計と内部実装の成熟に向けた一歩であり、より一貫性のある、Goらしいコードベースを構築するためのものです。

コミット

commit 360962420c484427bbc16e1f8699ee5cccc4f012
Author: Russ Cox <rsc@golang.org>
Date:   Fri Jan 16 14:58:14 2009 -0800

    casify, cleanup sys
    
    R=r
    OCL=22978
    CL=22984

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

https://github.com/golang/go/commit/360962420c484427bbc16e1f8699ee5cccc4f012

元コミット内容

casify, cleanup sys

変更の背景

このコミットの主な背景は、Go言語の初期段階におけるAPIの統一性とコードベースのクリーンアップです。Go言語は、その設計思想として「シンプルさ」と「一貫性」を重視しています。初期のGo言語のコードベースには、C言語の影響が強く残る部分があり、特に内部的な sys パッケージの関数名や引数処理には、Go言語の慣習とは異なる命名規則(snake_case や小文字始まりの関数名)が見られました。

具体的には、以下の点が変更の動機と考えられます。

  1. 命名規則の統一(camelCaseへの移行): Go言語では、エクスポートされる(外部からアクセス可能な)識別子は大文字で始まり、エクスポートされない(内部的な)識別子は小文字で始まる camelCase が推奨されています。このコミット以前は、sys パッケージ内のエクスポートされるべき関数が小文字で始まっていたり、snake_case であったりするケースがありました。これをGo言語の標準的な命名規則である camelCase に統一することで、コードの可読性と一貫性を向上させる狙いがあります。
  2. Goらしい引数処理への移行: C言語では argcargv を用いてコマンドライン引数を処理するのが一般的ですが、Go言語ではスライス([]string)を使って引数を扱うのがより自然です。sys.argc()sys.argv() といったC言語ライクな関数から sys.Args スライスへの移行は、Go言語のイディオムに合わせたAPI設計への転換を示しています。
  3. 標準ライブラリの活用: sys.stringtorune のような内部関数から、Go言語の標準ライブラリである unicode/utf8 パッケージの utf8.DecodeRuneInString への移行は、UTF-8文字列処理をより堅牢で標準的な方法で行うための変更です。これにより、Go言語の強力なUTF-8サポートを最大限に活用し、コードの品質と保守性を向上させることができます。
  4. コードベースの整理と最適化: src/runtime/rune.csrc/runtime/sys_file.c の一部機能の削除や再編成は、重複するコードの排除、機能の適切なモジュールへの移動、または不要になった機能の削除を通じて、コードベースをスリム化し、効率を高めるためのクリーンアップの一環です。

これらの変更は、Go言語がまだ初期段階であり、言語設計と実装が活発に進化していた時期に、その基盤をより強固でGoらしいものにするための重要なステップでした。

前提知識の解説

このコミットを理解するためには、以下のGo言語および関連するコンピュータサイエンスの概念に関する前提知識が必要です。

  1. Go言語の命名規則(camelCase:

    • Go言語では、識別子(変数名、関数名、型名など)の最初の文字が大文字である場合、その識別子はパッケージ外からアクセス可能(エクスポートされる)になります。
    • 最初の文字が小文字である場合、その識別子はパッケージ内でのみアクセス可能(エクスポートされない)です。
    • この規則は、Go言語のAPI設計において非常に重要であり、コードの可読性とモジュール性を高めます。
    • 一般的に、Go言語では snake_case (例: my_variable) ではなく camelCase (例: myVariable) が推奨されます。
  2. sys パッケージ(Go言語の内部インターフェース):

    • Go言語の初期には、sys という特別なパッケージが存在しました。これは、Goのランタイムやコンパイラが内部的に使用する低レベルな関数や変数を提供するためのものでした。
    • 通常のGoプログラムが直接 sys パッケージをインポートして使用することは想定されていませんでした。これは、C言語における標準ライブラリの一部が、OSのシステムコールを直接呼び出すためのラッパー関数を提供しているのと似ています。
    • このコミットに見られるように、sys パッケージ内の関数は、Go言語の進化とともに、よりGoらしいAPIに置き換えられたり、標準ライブラリの他のパッケージに統合されたりしていきました。
  3. コマンドライン引数(argc, argv vs. []string:

    • C言語の argcargv: C言語では、プログラムの main 関数は通常 int main(int argc, char *argv[]) の形式で定義されます。
      • argc (argument count) は、コマンドライン引数の数を表す整数です。
      • argv (argument vector) は、コマンドライン引数の文字列を指すポインタの配列です。argv[0] はプログラム自身の名前、argv[1] 以降が実際の引数になります。
    • Go言語の os.Args: Go言語では、os パッケージがコマンドライン引数を []string 型のスライスとして提供します。
      • os.Args は、プログラム名を含むすべてのコマンドライン引数を要素とする文字列スライスです。
      • len(os.Args) で引数の数を取得でき、os.Args[i] で個々の引数にアクセスできます。
      • このGoらしいアプローチは、C言語の argc/argv よりも型安全で扱いやすいとされています。
  4. UTF-8とルーン(Rune):

    • UTF-8: Unicode文字をエンコードするための可変長文字エンコーディングです。ASCII互換性があり、世界中のほとんどの文字を表現できます。
    • ルーン(Rune): Go言語における rune 型は、Unicodeコードポイントを表す組み込み型であり、実体は int32 のエイリアスです。Go言語の文字列はUTF-8でエンコードされたバイト列ですが、for range ループや unicode/utf8 パッケージを使用することで、バイト列をルーンとして効率的に処理できます。
    • sys.stringtorune は、文字列からルーンを抽出する内部関数でしたが、より汎用的な utf8.DecodeRuneInString に置き換えられました。
  5. アトミック操作とセマフォ:

    • アトミック操作: 複数のスレッドから同時にアクセスされても、その操作が中断されずに完全に実行されることを保証する操作です。cas (Compare And Swap) は代表的なアトミック操作の一つで、メモリ位置の値を特定の値と比較し、一致した場合にのみ新しい値に更新します。
    • セマフォ: 複数のプロセスやスレッドが共有リソースにアクセスする際の同期メカニズムです。セマフォはカウンタを持ち、semacquire (P操作) でカウンタを減らし、semrelease (V操作) でカウンタを増やします。カウンタが0の場合、semacquire はブロックします。
    • sync パッケージの Mutex (ミューテックス) は、セマフォを基盤として実装されることが多く、共有リソースへの排他的アクセスを保証します。

これらの概念を理解することで、このコミットがGo言語の設計原則にどのように合致し、その進化にどのように貢献したかを深く把握できます。

技術的詳細

このコミットにおける技術的な変更は多岐にわたりますが、その中心にあるのはGo言語の内部APIの近代化と、よりGoらしいイディオムへの移行です。

  1. sys パッケージ関数の命名規則変更:

    • src/cmd/gc/sys.go および src/cmd/gc/sysimport.c において、sys パッケージ内の多くの関数が snake_case や小文字始まりから camelCase に変更されました。例えば、sys.exitsys.Exit に、sys.goschedsys.Gosched に、sys.isNaNsys.IsNaN になっています。
    • これは、Go言語の「エクスポートされる識別子は大文字で始まる」という命名規則に準拠するための変更です。sys パッケージは内部的なものですが、コンパイラやランタイムから参照されるため、一種の「エクスポートされた」インターフェースとして扱われるべきという思想が反映されています。
    • sysimport.c は、Goコンパイラが sys パッケージの関数をC言語側から参照するための定義を含んでおり、ここでも対応する関数名の変更が行われています。
  2. コマンドライン引数処理の変更:

    • src/lib/flag.gotest/args.go などで、コマンドライン引数の取得方法が sys.argc()sys.argv(i) から len(sys.Args)sys.Args[i] に変更されました。
    • これは、Go言語が提供する os.Args スライス(このコミットではまだ sys.Args として内部的に扱われているが、概念は同じ)を利用する、よりGoらしい引数処理への移行です。スライスはGo言語の強力な機能であり、引数の数や内容にアクセスする際に、C言語のポインタと配列の概念よりも直感的で安全です。
    • src/runtime/runtime.c では、args 関数が sys.Argssys.Envs というグローバルな Array 型の変数にコマンドライン引数と環境変数を格納するように変更されています。これにより、Goのコードからこれらの情報にアクセスする際のインターフェースが統一されました。
  3. UTF-8文字列処理の標準化:

    • src/lib/fmt/print.go, src/lib/reflect/type.go, src/lib/regexp/regexp.go などで、sys.stringtorune 関数が utf8.DecodeRuneInString に置き換えられました。
    • sys.stringtorune は、Go言語の初期に内部的に使用されていた文字列からルーンをデコードする関数でしたが、unicode/utf8 パッケージが提供する utf8.DecodeRuneInString は、より堅牢で標準的なUTF-8デコード機能を提供します。この変更により、Go言語の文字列処理がより標準的で信頼性の高いものになりました。
    • これに伴い、src/runtime/rune.c にあった charntorunesys.bytestorune, sys.stringtorune といった関数が削除されています。これは、これらの機能が unicode/utf8 パッケージに統合されたことを示唆しています。
  4. src/runtime/sys_file.c の削除:

    • src/runtime/sys_file.c ファイル全体が削除されました。このファイルには、sys.readfilesys.writefile といったファイルI/Oを行う内部関数が含まれていました。
    • この削除は、これらのファイルI/O機能がGo言語の標準ライブラリ(os パッケージなど)に統合され、より高レベルで安全なAPIとして提供されるようになったことを意味します。低レベルなC言語のシステムコールを直接ラップする内部ファイルが不要になったと考えられます。
  5. セマフォ関数の直接利用:

    • src/lib/sync/mutex.go および src/lib/sync/mutex_test.go では、sys.semacquiresys.semrelease の呼び出しが、semacquiresemrelease に直接変更されています。
    • これは、sync パッケージがセマフォ操作を直接内部で扱うようになり、sys パッケージを介する必要がなくなったことを示唆しています。これにより、sync パッケージの独立性が高まり、内部実装の詳細がより適切にカプセル化されます。

これらの変更は、Go言語がその初期段階で、C言語の影響から脱却し、独自の強力な標準ライブラリと一貫した設計原則を持つ言語へと進化していく過程を明確に示しています。

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

このコミットにおけるコアとなる変更は、主に sys パッケージの関数名の変更と、コマンドライン引数およびUTF-8文字列処理のAPI変更に集約されます。

1. sys パッケージ関数の命名規則変更の例 (doc/progs/cat.go)

--- a/doc/progs/cat.go
+++ b/doc/progs/cat.go
@@ -16,7 +16,7 @@ func cat(file *fd.FD) {
 		switch nr, er := file.Read(buf); true {
 		case nr < 0:
 			print("error reading from ", file.String(), ": ", er.String(), "\n");
-			sys.exit(1);
+			sys.Exit(1);
 		case nr == 0:  // EOF
 			return;
 		case nr > 0:
@@ -36,7 +36,7 @@ func main() {
 		file, err := fd.Open(flag.Arg(i), 0, 0);
 		if file == nil {
 			print("can't open ", flag.Arg(i), ": error ", err, "\n");
-			sys.exit(1);
+			sys.Exit(1);
 		}
 		cat(file);
 		file.Close();

2. コマンドライン引数処理の変更の例 (src/lib/flag.go)

--- a/src/lib/flag.go
+++ b/src/lib/flag.go
@@ -239,13 +239,13 @@ export func PrintDefaults() {
 }
 
 export func Usage() {
-	if sys.argc() > 0 {
-		print("Usage of ", sys.argv(0), ": \n");
+	if len(sys.Args) > 0 {
+		print("Usage of ", sys.Args[0], ": \n");
 	} else {
 		print("Usage: \n");
 	}
 	PrintDefaults();
-	sys.exit(1);
+	sys.Exit(1);
 }
 
 export func NFlag() int {
@@ -254,14 +254,14 @@ export func NFlag() int {
 
 export func Arg(i int) string {
 	i += flags.first_arg;
-	if i < 0 || i >= sys.argc() {
+	if i < 0 || i >= len(sys.Args) {
 		return "";
 	}
-	return sys.argv(i)
+	return sys.Args[i]
 }
 
 export func NArg() int {
-	return sys.argc() - flags.first_arg
+	return len(sys.Args) - flags.first_arg
 }
 
 func add(name string, value _Value, usage string) {
@@ -339,7 +339,7 @@ export func StringVar(p *string, name, value string, usage string) {
 
 func (f *allFlags) ParseOne(index int) (ok bool, next int)
 {
-	s := sys.argv(index);
+	s := sys.Args[index];
 	f.first_arg = index;  // until proven otherwise
 	if len(s) == 0 {
 		return false, -1
@@ -398,11 +398,11 @@ func (f *allFlags) ParseOne(index int) (ok bool, next int)
 		}
 	} else {
 		// It must have a value, which might be the next argument.
-		if !has_value && index < sys.argc()-1 {
+		if !has_value && index < len(sys.Args)-1 {
 			// value is the next arg
 			has_value = true;
 			index++;
-			value = sys.argv(index);
+			value = sys.Args[index];
 		}
 		if !has_value {
 			print("flag needs an argument: -", name, "\n");
@@ -433,7 +433,7 @@ func (f *allFlags) ParseOne(index int) (ok bool, next int)
 }
 
 export func Parse() {
-	for i := 1; i < sys.argc(); {
+	for i := 1; i < len(sys.Args); {
 		ok, next := flags.ParseOne(i);
 		if next > 0 {
 			flags.first_arg = next;

3. UTF-8文字列処理の標準化の例 (src/lib/fmt/print.go)

--- a/src/lib/fmt/print.go
+++ b/src/lib/fmt/print.go
@@ -14,6 +14,7 @@ import (
 	"io";
 	"reflect";
 	"os";
+	"utf8";
 )
 
 // Representation of printer state passed to custom formatters.
@@ -438,7 +439,7 @@ func (p *pp) doprintf(format string, v reflect.StructValue) {
 	end := len(format) - 1;
 	fieldnum := 0;	// we process one field per non-trivial format
 	for i := 0; i <= end;  {
-		c, w := sys.stringtorune(format, i);
+		c, w := utf8.DecodeRuneInString(format, i);
 		if c != '%' || i == end {
 			p.add(c);
 			i += w;
@@ -469,7 +470,7 @@ func (p *pp) doprintf(format string, v reflect.StructValue) {
 		if i < end && format[i] == '.' {
 			p.fmt.prec, p.fmt.prec_present, i = parsenum(format, i+1, end);
 		}
-		c, w = sys.stringtorune(format, i);
+		c, w = utf8.DecodeRuneInString(format, i);
 		i += w;
 		// percent is special - absorbs no operand
 		if c == '%' {

4. src/runtime/rune.c の削除

src/runtime/rune.c ファイル全体が削除されています。これは、このファイルで提供されていたルーン関連の低レベル関数が、Go言語の標準ライブラリである unicode/utf8 パッケージに統合されたことを示しています。

5. src/runtime/sys_file.c の削除

src/runtime/sys_file.c ファイル全体が削除されています。このファイルは、sys.readfilesys.writefile といったファイルI/Oの内部関数を含んでいましたが、これらの機能はGo言語の os パッケージなどの標準ライブラリに移行されました。

コアとなるコードの解説

1. sys パッケージ関数の命名規則変更

doc/progs/cat.go の例では、sys.exit(1)sys.Exit(1) に変更されています。これは、Go言語の命名規則に従い、パッケージ外から参照される関数(この場合は sys パッケージの Exit 関数)の最初の文字を大文字にする変更です。これにより、Go言語のコードベース全体で一貫した命名スタイルが確立され、どの関数がエクスポートされているかが一目でわかるようになります。

2. コマンドライン引数処理の変更

src/lib/flag.go の例では、sys.argc()sys.argv(i) がそれぞれ len(sys.Args)sys.Args[i] に置き換えられています。

  • sys.argc() はC言語の argc に相当し、引数の数を返していました。
  • sys.argv(i) はC言語の argv[i] に相当し、i 番目の引数を返していました。
  • 新しい sys.Args はGo言語のスライスであり、len(sys.Args) でスライスの長さを取得し、sys.Args[i] でスライスの要素にアクセスします。 この変更は、Go言語がC言語の低レベルなインターフェースから脱却し、より抽象的で安全なスライスというデータ構造を使ってコマンドライン引数を扱うようになったことを示しています。これにより、コードの可読性が向上し、配列の境界外アクセスなどのエラーを防ぎやすくなります。

3. UTF-8文字列処理の標準化

src/lib/fmt/print.go の例では、sys.stringtorune(format, i)utf8.DecodeRuneInString(format, i) に変更されています。

  • sys.stringtorune は、Go言語の初期に内部的に使用されていた、文字列の指定されたオフセットからルーンをデコードする関数でした。
  • utf8.DecodeRuneInString は、Go言語の標準ライブラリである unicode/utf8 パッケージに含まれる関数で、UTF-8エンコードされた文字列から次のルーンとそのバイト幅を返します。 この変更は、Go言語が内部的なカスタム実装から、より標準的で広くテストされた unicode/utf8 パッケージの機能に移行したことを意味します。これにより、UTF-8文字列処理の正確性と堅牢性が向上し、将来的なUnicodeの変更にも対応しやすくなります。

4. src/runtime/rune.c および src/runtime/sys_file.c の削除

これらのファイルの削除は、Go言語のランタイムが進化し、低レベルなC言語の実装が不要になったことを示しています。ルーン処理は unicode/utf8 パッケージに、ファイルI/Oは os パッケージなどの標準ライブラリにそれぞれ統合され、より高レベルでGoらしいAPIとして提供されるようになりました。これは、Go言語がC言語のランタイムに依存する部分を減らし、より自己完結的でポータブルな言語へと成長していく過程の一部です。

これらの変更は、Go言語がその初期段階で、C言語の影響から脱却し、独自の強力な標準ライブラリと一貫した設計原則を持つ言語へと進化していく過程を明確に示しています。

関連リンク

参考にした情報源リンク

  • Go言語のソースコード (GitHub): https://github.com/golang/go
  • Go言語のコミット履歴: https://github.com/golang/go/commits/master
  • Go言語の初期の設計に関する議論やメーリングリストのアーカイブ (Go言語の進化の背景を理解する上で有用): https://groups.google.com/g/golang-nuts
  • Go言語の sys パッケージに関する非公式な解説 (初期のGo言語の内部構造を理解する上で参考になる場合があります): (Web検索で "golang sys package" などで検索)
  • C言語の argcargv に関する情報: (C言語のチュートリアルやリファレンス)
  • UTF-8エンコーディングに関する情報: (Unicode ConsortiumのウェブサイトやUTF-8に関する技術記事)