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

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

このコミットは、Go言語の標準ライブラリosパッケージにおけるPlan 9オペレーティングシステム固有の実装を、Go 1のAPI仕様に準拠させるための変更を含んでいます。具体的には、内部で使用される構造体や関数の可視性を調整し、エラーハンドリングを標準化しています。

コミット

os: Plan 9におけるGo 1 APIへの準拠

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

https://github.com/golang/go/commit/90626864dc965627590232adbed84949e3ba0e06

元コミット内容

os: conform to Go 1 API on Plan 9

R=golang-dev, r, bradfitz, r
CC=golang-dev
https://golang.org/cl/6117062

変更の背景

このコミットは、Go言語がバージョン1.0のリリースに向けてAPIの安定化を進めていた時期に行われたものです。Go 1のリリースでは、将来にわたって互換性を保証する安定したAPIセットを提供することが目標とされていました。そのため、Goの標準ライブラリ全体でAPIの命名規則、エラー処理、型定義などが厳密にレビューされ、必要に応じて変更が加えられました。

この特定のコミットは、osパッケージのPlan 9固有の実装が、Go 1のAPIガイドラインに完全に準拠していることを確認するためのものです。主な変更点は、外部に公開すべきでない内部的な型や関数を非公開(unexported)にし、エラーの返却方法をGo 1で推奨される形式に統一することにあります。これにより、GoのosパッケージがPlan 9環境においても、他のサポートされるOSと同様に一貫した振る舞いとAPIを提供できるようになります。

前提知識の解説

Go言語のAPI可視性(Exported vs. Unexported)

Go言語では、識別子(変数、関数、構造体、インターフェースなど)の最初の文字が大文字である場合、その識別子はパッケージ外からアクセス可能な「エクスポートされた(exported)」ものとなります。一方、最初の文字が小文字である場合、その識別子はパッケージ内でのみアクセス可能な「非エクスポートされた(unexported)」もの、つまり内部的な実装詳細となります。Go 1のAPI安定化では、ユーザーが直接利用すべきでない内部的な型や関数は非エクスポート化され、APIのシンプルさと安定性が追求されました。

Plan 9オペレーティングシステム

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの概念をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルシステムとして表現するという思想を持っています。Go言語は、その設計思想の一部をPlan 9から継承しており、初期からPlan 9に対するサポートが提供されていました。このコミットは、GoがPlan 9環境で適切に動作し、かつGo 1のAPI規約に沿っていることを保証するものです。

osパッケージ

Goの標準ライブラリのosパッケージは、オペレーティングシステムとの相互作用のための機能を提供します。これには、ファイル操作、ディレクトリ操作、プロセス管理、環境変数へのアクセスなどが含まれます。このパッケージは、異なるOS間で可能な限り一貫したインターフェースを提供するように設計されていますが、内部的には各OS固有の実装(例: dir_plan9.go, file_plan9.goなど)を持っています。

syscallパッケージ

syscallパッケージは、低レベルのオペレーティングシステムコールへのプリミティブなインターフェースを提供します。osパッケージのような高レベルの抽象化された機能の多くは、内部的にsyscallパッケージを利用しています。このコミットでは、エラーの表現にsyscallパッケージで定義されているエラーコード(例: syscall.EPLAN9)が使用されています。

FileInfoインターフェース

os.FileInfoインターフェースは、ファイルに関する情報(名前、サイズ、パーミッション、最終更新時刻など)を抽象的に表現するためのものです。os.Statos.Lstatなどの関数がこのインターフェースを実装した型を返します。

PathErrorおよびLinkError

Goのosパッケージでは、ファイルパスに関連する操作でエラーが発生した場合に、*os.PathError型のエラーを返すことが一般的です。これは、エラーが発生した操作、関連するパス、および根本的なエラーを構造化して提供します。同様に、リンク操作(ハードリンク、シンボリックリンク)でエラーが発生した場合は*os.LinkErrorが返されます。これらの特定のエラー型を返すことは、Go 1 APIの重要な側面であり、エラーハンドリングの予測可能性を高めます。

技術的詳細

このコミットで行われた主要な技術的変更は以下の通りです。

  1. 内部構造体と関数の非エクスポート化:

    • src/pkg/os/dir_plan9.goにおいて、公開されていたDir構造体がdirに、Qid構造体がqidに、UnmarshalDir関数がunmarshalDirにそれぞれリネームされました。これにより、これらの型と関数はosパッケージの外部からは直接アクセスできなくなり、内部実装の詳細として扱われるようになりました。これはGo 1のAPI設計原則に沿った変更であり、外部APIの安定性を高めます。
    • 関連して、これらの型を使用していた他のファイル(file_plan9.go, stat_plan9.go)も、新しい非エクスポート名を使用するように更新されています。
  2. エラーハンドリングの標準化:

    • src/pkg/os/file_plan9.goにおいて、これまでErrPlan9というカスタムエラーが返されていた箇所が、syscall.EPLAN9(Plan 9固有のシステムコールエラー)や、より具体的な*os.PathError*os.LinkErrorを返すように変更されました。
    • 例えば、Link, Symlink, Readlink, Chown, Lchown, File.Chownといった関数は、単にErrPlan9を返すのではなく、&LinkError{...}&PathError{...}といったGo 1で推奨される構造化されたエラー型を返すようになりました。これにより、エラーの種類と発生コンテキストがより明確になります。
    • StatTruncateなどの関数コメントに「If there is an error, it will be of type *PathError.」といったエラー型の明示的な記述が追加され、APIの振る舞いがより明確になりました。
  3. PathListSeparatorの明確化:

    • src/pkg/os/path_plan9.goにおいて、PathListSeparatorの定義が0から'\000'へと変更されました。これは、ヌル文字をより明示的に表現するための変更であり、コードの可読性と正確性を向上させます。

これらの変更は、Go 1のAPIが提供する一貫性と予測可能性を、Plan 9環境においても実現するための重要なステップです。

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

src/pkg/os/dir_plan9.go

--- a/src/pkg/os/dir_plan9.go
+++ b/src/pkg/os/dir_plan9.go
@@ -48,7 +48,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
 		if m < syscall.STATFIXLEN {
 			return result, &PathError{"readdir", file.name, errShortStat}
 		}
-		dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
+		dir, e := unmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
 		if e != nil {
 			return result, &PathError{"readdir", file.name, e}
 		}
@@ -73,12 +73,12 @@ func (file *File) readdirnames(n int) (names []string, err error) {
 	return
 }
 
-type Dir struct {
+type dir struct {
 	// system-modified data
 	Type uint16 // server type
 	Dev  uint32 // server subtype
 	// file data
-	Qid    Qid    // unique id from server
+	Qid    qid    // unique id from server
 	Mode   uint32 // permissions
 	Atime  uint32 // last read time
 	Mtime  uint32 // last write time
@@ -89,16 +89,16 @@ type Dir struct {
 	Muid   string // last modifier name
 }
 
-type Qid struct {
+type qid struct {
 	Path uint64 // the file server's unique identification for the file
 	Vers uint32 // version number for given Path
 	Type uint8  // the type of the file (syscall.QTDIR for example)
 }
 
-var nullDir = Dir{
+var nullDir = dir{
 	^uint16(0),
 	^uint32(0),
-	Qid{^uint64(0), ^uint32(0), ^uint8(0)},
+	qid{^uint64(0), ^uint32(0), ^uint8(0)},
 	^uint32(0),
 	^uint32(0),
 	^uint32(0),
@@ -111,12 +111,12 @@ var nullDir = Dir{
 
 // Null assigns members of d with special "don't care" values indicating
 // they should not be written by syscall.Wstat. 
-func (d *Dir) Null() {
+func (d *dir) Null() {
 	*d = nullDir
 }
 
 // pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b.
-func pdir(b []byte, d *Dir) []byte {
+func pdir(b []byte, d *dir) []byte {
 	n := len(b)
 	b = pbit16(b, 0) // length, filled in later	
 	b = pbit16(b, d.Type)
@@ -134,9 +134,9 @@ func pdir(b []byte, d *Dir) []byte {
 	return b
 }
 
-// UnmarshalDir reads a 9P Stat message from a 9P protocol message stored in b,
-// returning the corresponding Dir struct.\n-func UnmarshalDir(b []byte) (d *Dir, err error) {
+// unmarshalDir reads a 9P Stat message from a 9P protocol message stored in b,
+// returning the corresponding dir struct.\n+func unmarshalDir(b []byte) (d *dir, err error) {
 	n := uint16(0)
 	n, b = gbit16(b)
 
@@ -144,7 +144,7 @@ func UnmarshalDir(b []byte) (d *Dir, err error) {
 		return nil, errBadStat
 	}
 
-	d = new(Dir)
+	d = new(dir)
 	d.Type, b = gbit16(b)
 	d.Dev, b = gbit32(b)
 	d.Qid, b = gqid(b)
@@ -165,17 +165,17 @@ func UnmarshalDir(b []byte) (d *Dir, err error) {
 }
 
 // gqid reads the qid part of a 9P Stat message from a 9P protocol message stored in b,
-// returning the corresponding Qid struct and the remaining slice of b.\n-func gqid(b []byte) (Qid, []byte) {
-//	var q Qid
+// returning the corresponding qid struct and the remaining slice of b.\n+func gqid(b []byte) (qid, []byte) {
+//	var q qid
 	q.Path, b = gbit64(b)
 	q.Vers, b = gbit32(b)
 	q.Type, b = gbit8(b)
 	return q, b
 }
 
-// pqid appends a Qid struct q to a 9P message b.\n-func pqid(b []byte, q Qid) []byte {
+// pqid appends a qid struct q to a 9P message b.\n+func pqid(b []byte, q qid) []byte {
 	b = pbit64(b, q.Path)
 	b = pbit32(b, q.Vers)
 	b = pbit8(b, q.Type)

src/pkg/os/file_plan9.go

--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -338,32 +340,42 @@ func Pipe() (r *File, w *File, err error) {
 
 // not supported on Plan 9
 
-// Link creates a hard link.\n+// Link creates newname as a hard link to the oldname file.\n // If there is an error, it will be of type *LinkError.\n func Link(oldname, newname string) error {
-\treturn &LinkError{"link", oldname, newname, ErrPlan9}\n+\treturn &LinkError{"link", oldname, newname, syscall.EPLAN9}\n }\n \n // Symlink creates newname as a symbolic link to oldname.\n // If there is an error, it will be of type *LinkError.\n func Symlink(oldname, newname string) error {
-\treturn &LinkError{"symlink", oldname, newname, ErrPlan9}\n+\treturn &LinkError{"symlink", oldname, newname, syscall.EPLAN9}\n }\n \n+// Readlink returns the destination of the named symbolic link.\n+// If there is an error, it will be of type *PathError.\n func Readlink(name string) (string, error) {
-\treturn "", ErrPlan9\n+\treturn "", &PathError{"readlink", name, syscall.EPLAN9}\n }\n \n+// Chown changes the numeric uid and gid of the named file.\n+// If the file is a symbolic link, it changes the uid and gid of the link's target.\n+// If there is an error, it will be of type *PathError.\n func Chown(name string, uid, gid int) error {
-\treturn ErrPlan9\n+\treturn &PathError{"chown", name, syscall.EPLAN9}\n }\n \n+// Lchown changes the numeric uid and gid of the named file.\n+// If the file is a symbolic link, it changes the uid and gid of the file itself.\n+// If there is an error, it will be of type *PathError.\n func Lchown(name string, uid, gid int) error {
-\treturn ErrPlan9\n+\treturn &PathError{"lchown", name, syscall.EPLAN9}\n }\n \n+// Chown changes the numeric uid and gid of the named file.\n+// If there is an error, it will be of type *PathError.\n func (f *File) Chown(uid, gid int) error {
-\treturn ErrPlan9\n+\treturn &PathError{"chown", f.name, syscall.EPLAN9}\n }\n```

### `src/pkg/os/path_plan9.go`

```diff
--- a/src/pkg/os/path_plan9.go
+++ b/src/pkg/os/path_plan9.go
@@ -5,8 +5,8 @@
 package os
 
 const (
-	PathSeparator     = '/' // OS-specific path separator
-	PathListSeparator = 0   // OS-specific path list separator
+	PathSeparator     = '/'    // OS-specific path separator
+	PathListSeparator = '\000' // OS-specific path list separator
 )
 
 // IsPathSeparator returns true if c is a directory separator character.

コアとなるコードの解説

内部構造体と関数の非エクスポート化 (Dir -> dir, Qid -> qid, UnmarshalDir -> unmarshalDir)

Go言語では、パッケージの外部に公開するAPIは、その識別子を大文字で始めるという慣習があります。このコミットでは、DirQidUnmarshalDirといった識別子が小文字のdirqidunmarshalDirに変更されました。これは、これらの型や関数がosパッケージのPlan 9固有の実装の詳細であり、パッケージの外部から直接利用されることを意図していないためです。

Go 1のAPI安定化の目標の一つは、公開APIを最小限に保ち、内部実装の変更が外部のコードに影響を与えないようにすることでした。これらの変更は、その原則に則ったものであり、osパッケージのPlan 9実装の内部構造を隠蔽し、よりクリーンで安定した公開APIを提供することに貢献しています。

エラーハンドリングの標準化 (ErrPlan9から*PathError/*LinkError/syscall.EPLAN9へ)

以前は、Plan 9でサポートされていない操作に対して一律にErrPlan9というカスタムエラーが返されていました。しかし、Go 1のAPIでは、エラーの種類に応じて特定の構造化されたエラー型(例: *os.PathError*os.LinkError)を返すことが推奨されています。これにより、エラーを処理する側は、エラーの発生原因やコンテキストをより詳細に把握し、適切なエラーハンドリングを行うことができます。

このコミットでは、Link, Symlink, Readlink, Chown, Lchown, File.Chownといった関数が、それぞれの操作に対応する*LinkError*PathErrorを返すように変更されました。これらのエラー型は、エラーが発生した操作名、関連するファイルパス、そして根本的なシステムコールエラー(この場合はsyscall.EPLAN9)を含んでいます。これにより、エラーメッセージがより情報豊富になり、デバッグが容易になります。

また、StatTruncateなどの関数に、返されるエラーの型に関するコメントが追加されたことも重要です。これは、APIのドキュメントを改善し、開発者が関数の振る舞いをより正確に理解できるようにするためのものです。

PathListSeparatorの明確化

PathListSeparatorは、環境変数PATHのように、複数のパスを区切るために使用される文字を定義します。Plan 9では、このセパレータはヌル文字(\000)です。以前の定義では0とされていましたが、これは数値リテラルであり、文字としてのヌル文字をより明確に表現するために'\000'に変更されました。これは機能的な変更というよりも、コードの意図をより正確に表現するための改善です。

これらの変更は全体として、Go 1のAPIの堅牢性、一貫性、および使いやすさを向上させることを目的としています。

関連リンク

参考にした情報源リンク

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

このコミットは、Go言語の標準ライブラリosパッケージにおけるPlan 9オペレーティングシステム固有の実装を、Go 1のAPI仕様に準拠させるための変更を含んでいます。具体的には、内部で使用される構造体や関数の可視性を調整し、エラーハンドリングを標準化しています。

コミット

os: Plan 9におけるGo 1 APIへの準拠

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

https://github.com/golang/go/commit/90626864dc965627590232adbed84949e3ba0e06

元コミット内容

os: conform to Go 1 API on Plan 9

R=golang-dev, r, bradfitz, r
CC=golang-dev
https://golang.org/cl/6117062

変更の背景

このコミットは、Go言語がバージョン1.0のリリースに向けてAPIの安定化を進めていた時期に行われたものです。Go 1のリリースでは、将来にわたって互換性を保証する安定したAPIセットを提供することが目標とされていました。そのため、Goの標準ライブラリ全体でAPIの命名規則、エラー処理、型定義などが厳密にレビューされ、必要に応じて変更が加えられました。

この特定のコミットは、osパッケージのPlan 9固有の実装が、Go 1のAPIガイドラインに完全に準拠していることを確認するためのものです。主な変更点は、外部に公開すべきでない内部的な型や関数を非公開(unexported)にし、エラーの返却方法をGo 1で推奨される形式に統一することにあります。これにより、GoのosパッケージがPlan 9環境においても、他のサポートされるOSと同様に一貫した振る舞いとAPIを提供できるようになります。

前提知識の解説

Go言語のAPI可視性(Exported vs. Unexported)

Go言語では、識別子(変数、関数、構造体、インターフェースなど)の最初の文字が大文字である場合、その識別子はパッケージ外からアクセス可能な「エクスポートされた(exported)」ものとなります。一方、最初の文字が小文字である場合、その識別子はパッケージ内でのみアクセス可能な「非エクスポートされた(unexported)」もの、つまり内部的な実装詳細となります。Go 1のAPI安定化では、ユーザーが直接利用すべきでない内部的な型や関数は非エクスポート化され、APIのシンプルさと安定性が追求されました。

Plan 9オペレーティングシステム

Plan 9 from Bell Labsは、ベル研究所で開発された分散オペレーティングシステムです。Unixの概念をさらに推し進め、すべてのリソース(ファイル、デバイス、ネットワーク接続など)をファイルシステムとして表現するという思想を持っています。Go言語は、その設計思想の一部をPlan 9から継承しており、初期からPlan 9に対するサポートが提供されていました。このコミットは、GoがPlan 9環境で適切に動作し、かつGo 1のAPI規約に沿っていることを保証するものです。

osパッケージ

Goの標準ライブラリのosパッケージは、オペレーティングシステムとの相互作用のための機能を提供します。これには、ファイル操作、ディレクトリ操作、プロセス管理、環境変数へのアクセスなどが含まれます。このパッケージは、異なるOS間で可能な限り一貫したインターフェースを提供するように設計されていますが、内部的には各OS固有の実装(例: dir_plan9.go, file_plan9.goなど)を持っています。

syscallパッケージ

syscallパッケージは、低レベルのオペレーティングシステムコールへのプリミティブなインターフェースを提供します。osパッケージのような高レベルの抽象化された機能の多くは、内部的にsyscallパッケージを利用しています。このコミットでは、エラーの表現にsyscallパッケージで定義されているエラーコード(例: syscall.EPLAN9)が使用されています。

FileInfoインターフェース

os.FileInfoインターフェースは、ファイルに関する情報(名前、サイズ、パーミッション、最終更新時刻など)を抽象的に表現するためのものです。os.Statos.Lstatなどの関数がこのインターフェースを実装した型を返します。

PathErrorおよびLinkError

Goのosパッケージでは、ファイルパスに関連する操作でエラーが発生した場合に、*os.PathError型のエラーを返すことが一般的です。これは、エラーが発生した操作、関連するパス、および根本的なエラーを構造化して提供します。同様に、リンク操作(ハードリンク、シンボリックリンク)でエラーが発生した場合は*os.LinkErrorが返されます。これらの特定のエラー型を返すことは、Go 1 APIの重要な側面であり、エラーハンドリングの予測可能性を高めます。

技術的詳細

このコミットで行われた主要な技術的変更は以下の通りです。

  1. 内部構造体と関数の非エクスポート化:

    • src/pkg/os/dir_plan9.goにおいて、公開されていたDir構造体がdirに、Qid構造体がqidに、UnmarshalDir関数がunmarshalDirにそれぞれリネームされました。これにより、これらの型と関数はosパッケージの外部からは直接アクセスできなくなり、内部実装の詳細として扱われるようになりました。これはGo 1のAPI設計原則に沿った変更であり、外部APIの安定性を高めます。
    • 関連して、これらの型を使用していた他のファイル(file_plan9.go, stat_plan9.go)も、新しい非エクスポート名を使用するように更新されています。
  2. エラーハンドリングの標準化:

    • src/pkg/os/file_plan9.goにおいて、これまでErrPlan9というカスタムエラーが返されていた箇所が、syscall.EPLAN9(Plan 9固有のシステムコールエラー)や、より具体的な*os.PathError*os.LinkErrorを返すように変更されました。
    • 例えば、Link, Symlink, Readlink, Chown, Lchown, File.Chownといった関数は、単にErrPlan9を返すのではなく、&LinkError{...}&PathError{...}といったGo 1で推奨される構造化されたエラー型を返すようになりました。これにより、エラーの種類と発生コンテキストがより明確になります。
    • StatTruncateなどの関数コメントに「If there is an error, it will be of type *PathError.」といったエラー型の明示的な記述が追加され、APIの振る舞いがより明確になりました。
  3. PathListSeparatorの明確化:

    • src/pkg/os/path_plan9.goにおいて、PathListSeparatorの定義が0から'\000'へと変更されました。これは、ヌル文字をより明示的に表現するための変更であり、コードの可読性と正確性を向上させます。

これらの変更は、Go 1のAPIが提供する一貫性と予測可能性を、Plan 9環境においても実現するための重要なステップです。

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

src/pkg/os/dir_plan9.go

--- a/src/pkg/os/dir_plan9.go
+++ b/src/pkg/os/dir_plan9.go
@@ -48,7 +48,7 @@ func (file *File) readdir(n int) (fi []FileInfo, err error) {
 		if m < syscall.STATFIXLEN {
 			return result, &PathError{"readdir", file.name, errShortStat}
 		}
-		dir, e := UnmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
+		dir, e := unmarshalDir(d.buf[d.bufp : d.bufp+int(m)])
 		if e != nil {
 			return result, &PathError{"readdir", file.name, e}
 		}
@@ -73,12 +73,12 @@ func (file *File) readdirnames(n int) (names []string, err error) {
 	return
 }
 
-type Dir struct {
+type dir struct {
 	// system-modified data
 	Type uint16 // server type
 	Dev  uint32 // server subtype
 	// file data
-	Qid    Qid    // unique id from server
+	Qid    qid    // unique id from server
 	Mode   uint32 // permissions
 	Atime  uint32 // last read time
 	Mtime  uint32 // last write time
@@ -89,16 +89,16 @@ type Dir struct {
 	Muid   string // last modifier name
 }
 
-type Qid struct {
+type qid struct {
 	Path uint64 // the file server's unique identification for the file
 	Vers uint32 // version number for given Path
 	Type uint8  // the type of the file (syscall.QTDIR for example)
 }
 
-var nullDir = Dir{
+var nullDir = dir{
 	^uint16(0),
 	^uint32(0),
-	Qid{^uint64(0), ^uint32(0), ^uint8(0)},
+	qid{^uint64(0), ^uint32(0), ^uint8(0)},
 	^uint32(0),
 	^uint32(0),
 	^uint32(0),
@@ -111,12 +111,12 @@ var nullDir = Dir{
 
 // Null assigns members of d with special "don't care" values indicating
 // they should not be written by syscall.Wstat. 
-func (d *Dir) Null() {
+func (d *dir) Null() {
 	*d = nullDir
 }
 
 // pdir appends a 9P Stat message based on the contents of Dir d to a byte slice b.
-func pdir(b []byte, d *Dir) []byte {
+func pdir(b []byte, d *dir) []byte {
 	n := len(b)
 	b = pbit16(b, 0) // length, filled in later	
 	b = pbit16(b, d.Type)
@@ -134,9 +134,9 @@ func pdir(b []byte, d *Dir) []byte {
 	return b
 }
 
-// UnmarshalDir reads a 9P Stat message from a 9P protocol message stored in b,
-// returning the corresponding Dir struct.\n-func UnmarshalDir(b []byte) (d *Dir, err error) {
+// unmarshalDir reads a 9P Stat message from a 9P protocol message stored in b,
+// returning the corresponding dir struct.\n+func unmarshalDir(b []byte) (d *dir, err error) {
 	n := uint16(0)
 	n, b = gbit16(b)
 
@@ -144,7 +144,7 @@ func UnmarshalDir(b []byte) (d *Dir, err error) {
 		return nil, errBadStat
 	}
 
-	d = new(Dir)
+	d = new(dir)
 	d.Type, b = gbit16(b)
 	d.Dev, b = gbit32(b)
 	d.Qid, b = gqid(b)
@@ -165,17 +165,17 @@ func UnmarshalDir(b []byte) (d *Dir, err error) {
 }
 
 // gqid reads the qid part of a 9P Stat message from a 9P protocol message stored in b,
-// returning the corresponding Qid struct and the remaining slice of b.\n-func gqid(b []byte) (Qid, []byte) {
-//	var q Qid
+// returning the corresponding qid struct and the remaining slice of b.\n+func gqid(b []byte) (qid, []byte) {
+//	var q qid
 	q.Path, b = gbit64(b)
 	q.Vers, b = gbit32(b)
 	q.Type, b = gbit8(b)
 	return q, b
 }
 
-// pqid appends a Qid struct q to a 9P message b.\n-func pqid(b []byte, q Qid) []byte {
+// pqid appends a qid struct q to a 9P message b.\n+func pqid(b []byte, q qid) []byte {
 	b = pbit64(b, q.Path)
 	b = pbit32(b, q.Vers)
 	b = pbit8(b, q.Type)

src/pkg/os/file_plan9.go

--- a/src/pkg/os/file_plan9.go
+++ b/src/pkg/os/file_plan9.go
@@ -338,32 +340,42 @@ func Pipe() (r *File, w *File, err error) {
 
 // not supported on Plan 9
 
-// Link creates a hard link.\n+// Link creates newname as a hard link to the oldname file.\n // If there is an error, it will be of type *LinkError.\n func Link(oldname, newname string) error {
-\treturn &LinkError{"link", oldname, newname, ErrPlan9}\n+\treturn &LinkError{"link", oldname, newname, syscall.EPLAN9}\n }\n \n // Symlink creates newname as a symbolic link to oldname.\n // If there is an error, it will be of type *LinkError.\n func Symlink(oldname, newname string) error {
-\treturn &LinkError{"symlink", oldname, newname, ErrPlan9}\n+\treturn &LinkError{"symlink", oldname, newname, syscall.EPLAN9}\n }\n \n+// Readlink returns the destination of the named symbolic link.\n+// If there is an error, it will be of type *PathError.\n func Readlink(name string) (string, error) {
-\treturn "", ErrPlan9\n+\treturn "", &PathError{"readlink", name, syscall.EPLAN9}\n }\n \n+// Chown changes the numeric uid and gid of the named file.\n+// If the file is a symbolic link, it changes the uid and gid of the link's target.\n+// If there is an error, it will be of type *PathError.\n func Chown(name string, uid, gid int) error {
-\treturn ErrPlan9\n+\treturn &PathError{"chown", name, syscall.EPLAN9}\n }\n \n+// Lchown changes the numeric uid and gid of the named file.\n+// If the file is a symbolic link, it changes the uid and gid of the file itself.\n+// If there is an error, it will be of type *PathError.\n func Lchown(name string, uid, gid int) error {
-\treturn ErrPlan9\n+\treturn &PathError{"lchown", name, syscall.EPLAN9}\n }\n \n+// Chown changes the numeric uid and gid of the named file.\n+// If there is an error, it will be of type *PathError.\n func (f *File) Chown(uid, gid int) error {
-\treturn ErrPlan9\n+\treturn &PathError{"chown", f.name, syscall.EPLAN9}\n }\n```

### `src/pkg/os/path_plan9.go`

```diff
--- a/src/pkg/os/path_plan9.go
+++ b/src/pkg/os/path_plan9.go
@@ -5,8 +5,8 @@
 package os
 
 const (
-	PathSeparator     = '/' // OS-specific path separator
-	PathListSeparator = 0   // OS-specific path list separator
+	PathSeparator     = '/'    // OS-specific path separator
+	PathListSeparator = '\000' // OS-specific path list separator
 )
 
 // IsPathSeparator returns true if c is a directory separator character.

コアとなるコードの解説

内部構造体と関数の非エクスポート化 (Dir -> dir, Qid -> qid, UnmarshalDir -> unmarshalDir)

Go言語では、パッケージの外部に公開するAPIは、その識別子を大文字で始めるという慣習があります。このコミットでは、DirQidUnmarshalDirといった識別子が小文字のdirqidunmarshalDirに変更されました。これは、これらの型や関数がosパッケージのPlan 9固有の実装の詳細であり、パッケージの外部から直接利用されることを意図していないためです。

Go 1のAPI安定化の目標の一つは、公開APIを最小限に保ち、内部実装の変更が外部のコードに影響を与えないようにすることでした。これらの変更は、その原則に則ったものであり、osパッケージのPlan 9実装の内部構造を隠蔽し、よりクリーンで安定した公開APIを提供することに貢献しています。

エラーハンドリングの標準化 (ErrPlan9から*PathError/*LinkError/syscall.EPLAN9へ)

以前は、Plan 9でサポートされていない操作に対して一律にErrPlan9というカスタムエラーが返されていました。しかし、Go 1のAPIでは、エラーの種類に応じて特定の構造化されたエラー型(例: *os.PathError*os.LinkError)を返すことが推奨されています。これにより、エラーを処理する側は、エラーの発生原因やコンテキストをより詳細に把握し、適切なエラーハンドリングを行うことができます。

このコミットでは、Link, Symlink, Readlink, Chown, Lchown, File.Chownといった関数が、それぞれの操作に対応する*LinkError*PathErrorを返すように変更されました。これらのエラー型は、エラーが発生した操作名、関連するファイルパス、そして根本的なシステムコールエラー(この場合はsyscall.EPLAN9)を含んでいます。これにより、エラーメッセージがより情報豊富になり、デバッグが容易になります。

また、StatTruncateなどの関数に、返されるエラーの型に関するコメントが追加されたことも重要です。これは、APIのドキュメントを改善し、開発者が関数の振る舞いをより正確に理解できるようにするためのものです。

PathListSeparatorの明確化

PathListSeparatorは、環境変数PATHのように、複数のパスを区切るために使用される文字を定義します。Plan 9では、このセパレータはヌル文字(\000)です。以前の定義では0とされていましたが、これは数値リテラルであり、文字としてのヌル文字をより明確に表現するために'\000'に変更されました。これは機能的な変更というよりも、コードの意図をより正確に表現するための改善です。

これらの変更は全体として、Go 1のAPIの堅牢性、一貫性、および使いやすさを向上させることを目的としています。

関連リンク

参考にした情報源リンク