[インデックス 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
や小文字始まりの関数名)が見られました。
具体的には、以下の点が変更の動機と考えられます。
- 命名規則の統一(
camelCase
への移行): Go言語では、エクスポートされる(外部からアクセス可能な)識別子は大文字で始まり、エクスポートされない(内部的な)識別子は小文字で始まるcamelCase
が推奨されています。このコミット以前は、sys
パッケージ内のエクスポートされるべき関数が小文字で始まっていたり、snake_case
であったりするケースがありました。これをGo言語の標準的な命名規則であるcamelCase
に統一することで、コードの可読性と一貫性を向上させる狙いがあります。 - Goらしい引数処理への移行: C言語では
argc
とargv
を用いてコマンドライン引数を処理するのが一般的ですが、Go言語ではスライス([]string
)を使って引数を扱うのがより自然です。sys.argc()
やsys.argv()
といったC言語ライクな関数からsys.Args
スライスへの移行は、Go言語のイディオムに合わせたAPI設計への転換を示しています。 - 標準ライブラリの活用:
sys.stringtorune
のような内部関数から、Go言語の標準ライブラリであるunicode/utf8
パッケージのutf8.DecodeRuneInString
への移行は、UTF-8文字列処理をより堅牢で標準的な方法で行うための変更です。これにより、Go言語の強力なUTF-8サポートを最大限に活用し、コードの品質と保守性を向上させることができます。 - コードベースの整理と最適化:
src/runtime/rune.c
やsrc/runtime/sys_file.c
の一部機能の削除や再編成は、重複するコードの排除、機能の適切なモジュールへの移動、または不要になった機能の削除を通じて、コードベースをスリム化し、効率を高めるためのクリーンアップの一環です。
これらの変更は、Go言語がまだ初期段階であり、言語設計と実装が活発に進化していた時期に、その基盤をより強固でGoらしいものにするための重要なステップでした。
前提知識の解説
このコミットを理解するためには、以下のGo言語および関連するコンピュータサイエンスの概念に関する前提知識が必要です。
-
Go言語の命名規則(
camelCase
):- Go言語では、識別子(変数名、関数名、型名など)の最初の文字が大文字である場合、その識別子はパッケージ外からアクセス可能(エクスポートされる)になります。
- 最初の文字が小文字である場合、その識別子はパッケージ内でのみアクセス可能(エクスポートされない)です。
- この規則は、Go言語のAPI設計において非常に重要であり、コードの可読性とモジュール性を高めます。
- 一般的に、Go言語では
snake_case
(例:my_variable
) ではなくcamelCase
(例:myVariable
) が推奨されます。
-
sys
パッケージ(Go言語の内部インターフェース):- Go言語の初期には、
sys
という特別なパッケージが存在しました。これは、Goのランタイムやコンパイラが内部的に使用する低レベルな関数や変数を提供するためのものでした。 - 通常のGoプログラムが直接
sys
パッケージをインポートして使用することは想定されていませんでした。これは、C言語における標準ライブラリの一部が、OSのシステムコールを直接呼び出すためのラッパー関数を提供しているのと似ています。 - このコミットに見られるように、
sys
パッケージ内の関数は、Go言語の進化とともに、よりGoらしいAPIに置き換えられたり、標準ライブラリの他のパッケージに統合されたりしていきました。
- Go言語の初期には、
-
コマンドライン引数(
argc
,argv
vs.[]string
):- C言語の
argc
とargv
: 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
よりも型安全で扱いやすいとされています。
- C言語の
-
UTF-8とルーン(Rune):
- UTF-8: Unicode文字をエンコードするための可変長文字エンコーディングです。ASCII互換性があり、世界中のほとんどの文字を表現できます。
- ルーン(Rune): Go言語における
rune
型は、Unicodeコードポイントを表す組み込み型であり、実体はint32
のエイリアスです。Go言語の文字列はUTF-8でエンコードされたバイト列ですが、for range
ループやunicode/utf8
パッケージを使用することで、バイト列をルーンとして効率的に処理できます。 sys.stringtorune
は、文字列からルーンを抽出する内部関数でしたが、より汎用的なutf8.DecodeRuneInString
に置き換えられました。
-
アトミック操作とセマフォ:
- アトミック操作: 複数のスレッドから同時にアクセスされても、その操作が中断されずに完全に実行されることを保証する操作です。
cas
(Compare And Swap) は代表的なアトミック操作の一つで、メモリ位置の値を特定の値と比較し、一致した場合にのみ新しい値に更新します。 - セマフォ: 複数のプロセスやスレッドが共有リソースにアクセスする際の同期メカニズムです。セマフォはカウンタを持ち、
semacquire
(P操作) でカウンタを減らし、semrelease
(V操作) でカウンタを増やします。カウンタが0の場合、semacquire
はブロックします。 sync
パッケージのMutex
(ミューテックス) は、セマフォを基盤として実装されることが多く、共有リソースへの排他的アクセスを保証します。
- アトミック操作: 複数のスレッドから同時にアクセスされても、その操作が中断されずに完全に実行されることを保証する操作です。
これらの概念を理解することで、このコミットがGo言語の設計原則にどのように合致し、その進化にどのように貢献したかを深く把握できます。
技術的詳細
このコミットにおける技術的な変更は多岐にわたりますが、その中心にあるのはGo言語の内部APIの近代化と、よりGoらしいイディオムへの移行です。
-
sys
パッケージ関数の命名規則変更:src/cmd/gc/sys.go
およびsrc/cmd/gc/sysimport.c
において、sys
パッケージ内の多くの関数がsnake_case
や小文字始まりからcamelCase
に変更されました。例えば、sys.exit
はsys.Exit
に、sys.gosched
はsys.Gosched
に、sys.isNaN
はsys.IsNaN
になっています。- これは、Go言語の「エクスポートされる識別子は大文字で始まる」という命名規則に準拠するための変更です。
sys
パッケージは内部的なものですが、コンパイラやランタイムから参照されるため、一種の「エクスポートされた」インターフェースとして扱われるべきという思想が反映されています。 sysimport.c
は、Goコンパイラがsys
パッケージの関数をC言語側から参照するための定義を含んでおり、ここでも対応する関数名の変更が行われています。
-
コマンドライン引数処理の変更:
src/lib/flag.go
やtest/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.Args
とsys.Envs
というグローバルなArray
型の変数にコマンドライン引数と環境変数を格納するように変更されています。これにより、Goのコードからこれらの情報にアクセスする際のインターフェースが統一されました。
-
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
にあったcharntorune
やsys.bytestorune
,sys.stringtorune
といった関数が削除されています。これは、これらの機能がunicode/utf8
パッケージに統合されたことを示唆しています。
-
src/runtime/sys_file.c
の削除:src/runtime/sys_file.c
ファイル全体が削除されました。このファイルには、sys.readfile
やsys.writefile
といったファイルI/Oを行う内部関数が含まれていました。- この削除は、これらのファイルI/O機能がGo言語の標準ライブラリ(
os
パッケージなど)に統合され、より高レベルで安全なAPIとして提供されるようになったことを意味します。低レベルなC言語のシステムコールを直接ラップする内部ファイルが不要になったと考えられます。
-
セマフォ関数の直接利用:
src/lib/sync/mutex.go
およびsrc/lib/sync/mutex_test.go
では、sys.semacquire
とsys.semrelease
の呼び出しが、semacquire
とsemrelease
に直接変更されています。- これは、
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.readfile
や sys.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言語の公式ドキュメント: https://go.dev/doc/
- Go言語の命名規則に関する公式ガイドライン: https://go.dev/doc/effective_go#names
os
パッケージのドキュメント (コマンドライン引数os.Args
): https://pkg.go.dev/osunicode/utf8
パッケージのドキュメント: https://pkg.go.dev/unicode/utf8- Go言語の
sync
パッケージのドキュメント: https://pkg.go.dev/sync
参考にした情報源リンク
- 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言語の
argc
とargv
に関する情報: (C言語のチュートリアルやリファレンス) - UTF-8エンコーディングに関する情報: (Unicode ConsortiumのウェブサイトやUTF-8に関する技術記事)