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

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

このコミットは、Go言語の初期のコンパイラである6gにおけるエクスポート(公開)に関するバグが修正されたことを受けて、osパッケージ内のDirInfo型をdirInfo(非公開)に変更するものです。これにより、本来非公開であるべき内部構造体が正しく扱われるようになります。

コミット

commit 0c4010a74725e789321f86895403bbeddd11cb14
Author: Rob Pike <r@golang.org>
Date:   Tue Feb 10 16:44:04 2009 -0800

    change DirInfo->dirInfo now that 6g export bug is fixed
    
    R=rsc
    DELTA=4  (0 added, 0 deleted, 4 changed)
    OCL=24788
    CL=24805
---
 src/lib/os/dir_amd64_darwin.go | 2 +-\
 src/lib/os/dir_amd64_linux.go  | 2 +-\
 src/lib/os/file.go             | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/lib/os/dir_amd64_darwin.go b/src/lib/os/dir_amd64_darwin.go
index 04d3a90c6f..2821076dc6 100644
--- a/src/lib/os/dir_amd64_darwin.go
+++ b/src/lib/os/dir_amd64_darwin.go
@@ -18,7 +18,7 @@ const (
 func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {
 	// If this fd has no dirinfo, create one.
 	if fd.dirinfo == nil {
-\t\tfd.dirinfo = new(DirInfo);\n+\t\tfd.dirinfo = new(dirInfo);\n \t\t// The buffer must be at least a block long.\n \t\t// TODO(r): use fstatfs to find fs block size.\n \t\tfd.dirinfo.buf = make([]byte, blockSize);\ndiff --git a/src/lib/os/dir_amd64_linux.go b/src/lib/os/dir_amd64_linux.go
index a5b8a7ba2e..f73febc4c7 100644
--- a/src/lib/os/dir_amd64_linux.go
+++ b/src/lib/os/dir_amd64_linux.go
@@ -27,7 +27,7 @@ func clen(n []byte) int {
 func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {
 	// If this fd has no dirinfo, create one.
 	if fd.dirinfo == nil {
-\t\tfd.dirinfo = new(DirInfo);\n+\t\tfd.dirinfo = new(dirInfo);\n \t\t// The buffer must be at least a block long.\n \t\t// TODO(r): use fstatfs to find fs block size.\n \t\tfd.dirinfo.buf = make([]byte, blockSize);\ndiff --git a/src/lib/os/file.go b/src/lib/os/file.go
index 2a4bc6723d..a0fc4bb679 100644
--- a/src/lib/os/file.go
+++ b/src/lib/os/file.go
@@ -8,7 +8,7 @@ import syscall "syscall"\n import os "os"\n \n // Auxiliary information if the FD describes a directory\n-type DirInfo struct {\t// TODO(r): 6g bug means this can\'t be private\n+type dirInfo struct {\t// TODO(r): 6g bug means this can\'t be private\n \tbuf\t[]byte;\t// buffer for directory I/O\n \tnbuf\tint64;\t// length of buf; return value from Getdirentries\n \tbufp\tint64;\t// location of next record in buf.\n@@ -18,7 +18,7 @@ type DirInfo struct {\t// TODO(r): 6g bug means this can\'t be private\n type FD struct {\n \tfd int64;\n \tname\tstring;\n-\tdirinfo\t*DirInfo;\t// nil unless directory being read\n+\tdirinfo\t*dirInfo;\t// nil unless directory being read\n }\n \n func (fd *FD) Fd() int64 {\n```

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

[https://github.com/golang/go/commit/0c4010a74725e789321f86895403bbeddd11cb14](https://github.com/golang/go/commit/0c4010a74725e789321f86895403bbeddd11cb14)

## 元コミット内容

change DirInfo->dirInfo now that 6g export bug is fixed


## 変更の背景

このコミットの背景には、Go言語の初期のコンパイラである`6g`における「エクスポートバグ」の存在があります。Go言語では、識別子(変数名、関数名、型名など)の最初の文字が大文字である場合、その識別子はパッケージ外からアクセス可能な「エクスポートされた(公開された)」ものとして扱われます。一方、最初の文字が小文字である場合、その識別子はパッケージ内でのみアクセス可能な「エクスポートされていない(非公開の)」ものとして扱われます。

`os`パッケージ内の`DirInfo`型は、ディレクトリの読み取りに関する補助情報を保持するための内部構造体であり、本来はパッケージ外部に公開されるべきではありませんでした。しかし、当時の`6g`コンパイラには、このエクスポートルールに関するバグが存在し、`DirInfo`のように大文字で始まる識別子であっても、特定の条件下で正しくエクスポートされない、あるいは意図せずエクスポートされてしまうといった問題があった可能性があります。

このバグが修正されたことにより、開発者は`DirInfo`を本来あるべき非公開の`dirInfo`に変更することが可能になりました。これにより、Go言語のエクスポートルールに則った、より適切なカプセル化が実現され、パッケージの内部実装が外部に漏れることを防ぎ、APIの安定性と保守性を向上させることができます。

## 前提知識の解説

### Go言語のエクスポートルール

Go言語におけるエクスポートルールは非常にシンプルかつ強力です。
*   **公開(Exported)**: 識別子の最初の文字が大文字の場合、その識別子はパッケージ外からアクセス可能です。これは、他の言語における`public`に相当します。
*   **非公開(Unexported)**: 識別子の最初の文字が小文字の場合、その識別子はパッケージ内でのみアクセス可能です。これは、他の言語における`private`や`internal`に相当します。

このルールは、型、フィールド、関数、変数など、すべての識別子に適用されます。これにより、開発者は意図的にAPIを設計し、内部実装の詳細を隠蔽することができます。

### `os`パッケージとファイルディスクリプタ (`FD`)

`os`パッケージは、オペレーティングシステムとのインタラクション(ファイル操作、プロセス管理など)を提供します。
*   `FD`型: `os`パッケージにおける`FD`(File Descriptor)型は、開かれたファイルやディレクトリを表す構造体です。これは、Unix系のシステムコールにおけるファイルディスクリプタの概念をGo言語で抽象化したものです。
*   `Readdirnames`関数: `FD`型に紐づくメソッド(または関数)で、ディレクトリの内容を読み取り、その中のエントリ名(ファイル名やサブディレクトリ名)のリストを返します。

### `6g`コンパイラ

`6g`は、Go言語の初期に存在したコンパイラの一つです。Go言語は当初、Plan 9というオペレーティングシステムのために開発されたツールチェインをベースにしており、`6g`はそのツールチェインの一部として、x86-64アーキテクチャ(AMD64)向けのGoコードをコンパイルするために使用されていました。Go言語の進化とともに、コンパイラはより汎用的な`go tool compile`へと移行していきましたが、このコミットが作成された2009年当時は`6g`が主要なコンパイラでした。

## 技術的詳細

このコミットの技術的な核心は、Go言語のエクスポートルールと、それを当時の`6g`コンパイラがどのように扱っていたかという点にあります。

本来、`os`パッケージの内部でディレクトリの読み取りを補助するために使われる`DirInfo`構造体は、パッケージの外部から直接アクセスされるべきではありません。これは、`DirInfo`が`Readdirnames`のような公開APIの内部実装の詳細であり、その構造が将来変更される可能性があるためです。もし`DirInfo`が公開されたままだと、外部のコードがそれに依存してしまい、`os`パッケージの内部変更が外部に影響を与える「破壊的変更」となるリスクがあります。

コミットメッセージにある「6g export bug is fixed」という記述は、`6g`コンパイラが、本来非公開であるべき型(例えば、小文字で始まる`dirInfo`)を誤って公開してしまったり、あるいはその逆で、大文字で始まる型(`DirInfo`)が正しく公開されない、または内部で利用する際に問題が生じる、といったバグを抱えていたことを示唆しています。

このバグが修正されたことで、Go言語の設計思想である「大文字で始まる識別子は公開、小文字で始まる識別子は非公開」というルールがコンパイラによって正しく強制されるようになりました。これにより、開発者は安心して内部構造体を非公開に設定できるようになり、`DirInfo`を`dirInfo`にリネームすることで、`os`パッケージの内部実装が外部から隠蔽され、より堅牢なAPI設計が実現されました。

具体的には、`src/lib/os/file.go`で`DirInfo`型が`dirInfo`型にリネームされ、それに伴い`FD`構造体の`dirinfo`フィールドの型も`*DirInfo`から`*dirInfo`に変更されています。この変更は、`src/lib/os/dir_amd64_darwin.go`と`src/lib/os/dir_amd64_linux.go`内の`Readdirnames`関数における`new(DirInfo)`の呼び出し箇所にも反映され、`new(dirInfo)`へと修正されています。これにより、`os`パッケージの内部で利用される`dirInfo`が、Go言語のルールに従って正しく非公開として扱われるようになりました。

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

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

1.  `src/lib/os/dir_amd64_darwin.go`
2.  `src/lib/os/dir_amd64_linux.go`
3.  `src/lib/os/file.go`

それぞれの変更点は以下の通りです。

### `src/lib/os/dir_amd64_darwin.go` および `src/lib/os/dir_amd64_linux.go`

これらのファイルは、それぞれmacOS (darwin) と Linux 環境におけるディレクトリ読み取りの低レベルな実装を含んでいます。変更内容は同一です。

```diff
--- a/src/lib/os/dir_amd64_darwin.go
+++ b/src/lib/os/dir_amd64_darwin.go
@@ -18,7 +18,7 @@ const (
 func Readdirnames(fd *FD, count int) (names []string, err *os.Error) {
 	// If this fd has no dirinfo, create one.
 	if fd.dirinfo == nil {
-\t\tfd.dirinfo = new(DirInfo);\n+\t\tfd.dirinfo = new(dirInfo);\n \t\t// The buffer must be at least a block long.\n \t\t// TODO(r): use fstatfs to find fs block size.\n \t\tfd.dirinfo.buf = make([]byte, blockSize);\

new(DirInfo)new(dirInfo)に変更されています。これは、file.goDirInfo型がdirInfo型にリネームされたことに伴う修正です。

src/lib/os/file.go

このファイルは、osパッケージの基本的なファイル操作に関連する型定義を含んでいます。

--- a/src/lib/os/file.go
+++ b/src/lib/os/file.go
@@ -8,7 +8,7 @@ import syscall "syscall"\n import os "os"\n \n // Auxiliary information if the FD describes a directory\n-type DirInfo struct {\t// TODO(r): 6g bug means this can\'t be private\n+type dirInfo struct {\t// TODO(r): 6g bug means this can\'t be private\n \tbuf\t[]byte;\t// buffer for directory I/O\n \tnbuf\tint64;\t// length of buf; return value from Getdirentries\n \tbufp\tint64;\t// location of next record in buf.\n@@ -18,7 +18,7 @@ type DirInfo struct {\t// TODO(r): 6g bug means this can\'t be private\n type FD struct {\n \tfd int64;\n \tname\tstring;\n-\tdirinfo\t*DirInfo;\t// nil unless directory being read\n+\tdirinfo\t*dirInfo;\t// nil unless directory being read\n }\n \n func (fd *FD) Fd() int64 {\
  • type DirInfo structtype dirInfo struct に変更されています。これにより、DirInfo型が非公開になりました。
  • FD構造体内のdirinfoフィールドの型が *DirInfo から *dirInfo に変更されています。これは、DirInfo型のリネームに合わせた修正です。

コアとなるコードの解説

このコミットの核心は、src/lib/os/file.goにおけるDirInfo型からdirInfo型へのリネームです。

元のコードでは、DirInfoという大文字で始まる型名が使われていました。Go言語のエクスポートルールに従えば、これはパッケージ外部に公開される型となります。しかし、コメントにある// TODO(r): 6g bug means this can't be privateという記述から、開発者(Rob Pike)は本来この型を非公開にしたかったものの、当時の6gコンパイラのバグのためにそれができなかったことが伺えます。このバグが修正されたことで、型名を小文字で始まるdirInfoに変更し、Go言語の設計原則に沿ってこの型を非公開にすることが可能になりました。

dirInfo型は、FD構造体のdirinfoフィールドとして埋め込まれており、Readdirnames関数内でディレクトリの読み取りバッファや現在の読み取り位置などの内部状態を管理するために使用されます。これらの情報はosパッケージの内部実装の詳細であり、外部のコードが直接アクセスする必要はありません。非公開にすることで、osパッケージの内部変更が外部に影響を与えるリスクを減らし、APIの安定性を保つことができます。

src/lib/os/dir_amd64_darwin.gosrc/lib/os/dir_amd64_linux.goにおける変更は、このdirInfo型のリネームに伴うものです。Readdirnames関数内でFDdirinfoフィールドがnilの場合に新しいdirInfoインスタンスを作成する際に、new(DirInfo)ではなくnew(dirInfo)を呼び出すように修正されています。これにより、コンパイラが正しく非公開のdirInfo型をインスタンス化するようになります。

この変更は、Go言語の初期段階におけるコンパイラの成熟度と、言語設計の意図をコンパイラが正確に反映できるようになるまでの過程を示しています。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント
  • Go言語のソースコード(特にosパッケージの初期のコミット履歴)
  • Go言語のコンパイラに関する歴史的な議論やブログ記事("6g export bug"に関する具体的な情報は見つけられませんでしたが、Goの初期のコンパイラに関する一般的な情報から推測しました。)
  • GitHubのコミットページ: https://github.com/golang/go/commit/0c4010a74725e789321f86895403bbeddd11cb14