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

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

このコミットは、Go言語の標準ライブラリであるosパッケージにおけるエラー述語関数(IsExist, IsNotExist, IsPermission)のドキュメントの重複を解消することを目的としています。具体的には、これらの関数の公開APIとしてのドキュメントをsrc/pkg/os/error.goに集約し、各プラットフォーム固有の実装ファイル(src/pkg/os/error_plan9.go, src/pkg/os/error_posix.go, src/pkg/os/error_windows.go)からはドキュメントを削除し、関数名を非公開(unexported)なものに変更しています。

コミット

  • コミットハッシュ: 4ca59a010e42978d168a6c92335557a088284b99
  • 作者: Shenghou Ma minux.ma@gmail.com
  • コミット日時: 2012年3月13日 火曜日 13:48:07 +0800

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

https://github.com/golang/go/commit/4ca59a010e42978d168a6c92335557a088284b99

元コミット内容

os: remove document duplication in error predicate functions

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

変更の背景

Go言語のosパッケージには、ファイル操作などで発生するエラーが特定の条件(例: ファイルが存在するか、パーミッションエラーか)を満たすかどうかを判定するための述語関数が提供されています。これらはIsExist, IsNotExist, IsPermissionといった形で公開されています。

これらの関数は、内部的には各オペレーティングシステム(OS)の特性に合わせて異なる実装を持っていました。例えば、POSIX準拠のシステム(Linux, macOSなど)ではsyscall.EEXISTのようなエラーコードをチェックし、Windowsではsyscall.ERROR_FILE_EXISTSなどをチェックします。

変更前は、これらの公開関数とそのドキュメントが、src/pkg/os/error.go(共通部分)と、src/pkg/os/error_plan9.go, src/pkg/os/error_posix.go, src/pkg/os/error_windows.goといった各プラットフォーム固有のファイルの両方に存在していました。これにより、同じ関数のドキュメントが複数箇所に記述され、冗長性が生じていました。ドキュメントの更新や修正が必要になった場合、複数のファイルを変更する必要があり、保守性の低下やドキュメントの不整合を引き起こす可能性がありました。

このコミットは、このようなドキュメントの重複を解消し、コードの保守性と一貫性を向上させることを目的としています。

前提知識の解説

Go言語のosパッケージ

osパッケージは、オペレーティングシステムが提供する機能(ファイルシステム操作、プロセス管理、環境変数など)へのプラットフォームに依存しないインターフェースを提供します。ファイルやディレクトリの作成、読み書き、削除、情報の取得など、多くの基本的なシステム操作がこのパッケージを通じて行われます。

Go言語におけるエラーハンドリング

Go言語では、エラーは組み込みのerrorインターフェースによって表現されます。関数がエラーを返す場合、通常は戻り値の最後の要素としてerror型を返します。呼び出し元は、このエラーがnilでない場合にエラーが発生したと判断し、適切に処理します。

osパッケージでは、ファイルパスに関連するエラーをラップするために*PathErrorという構造体が使われることがあります。これは、エラーが発生した操作、パス、そして元のエラーを保持し、より詳細な情報を提供します。

エラー述語関数 (Error Predicate Functions)

エラー述語関数とは、特定のエラーが特定の条件を満たすかどうかを判定するための関数です。osパッケージのIsExist, IsNotExist, IsPermissionなどがこれに該当します。これらの関数は、単にerrorインターフェースを実装した型をチェックするだけでなく、基となるシステムコールエラー(syscallパッケージで定義されるようなOS固有のエラーコード)も考慮して、より汎用的なエラー判定を提供します。これにより、開発者はOSの違いを意識することなく、エラーの種類を判定できます。

Go言語における公開 (Exported) と非公開 (Unexported)

Go言語では、識別子(変数、関数、型など)の最初の文字が大文字である場合、その識別子はパッケージの外部からアクセス可能な「公開 (exported)」なものとなります。一方、最初の文字が小文字である場合、その識別子はパッケージ内部でのみアクセス可能な「非公開 (unexported)」なものとなります。この仕組みは、APIの設計において、外部に公開するインターフェースと内部実装を明確に区別するために非常に重要です。

技術的詳細

このコミットの技術的な核心は、Go言語の公開/非公開のルールと、共通インターフェースとプラットフォーム固有実装の分離にあります。

  1. ドキュメントの集約:

    • src/pkg/os/error.goは、osパッケージのエラー関連の共通ロジックと公開APIを定義するファイルです。このコミットでは、IsExist, IsNotExist, IsPermissionという公開関数のドキュメント(コメント)がこのファイルにのみ記述されるように変更されました。これにより、これらの関数の公式な振る舞いや説明は一箇所で管理されることになります。
  2. プラットフォーム固有関数の非公開化:

    • src/pkg/os/error_plan9.go, src/pkg/os/error_posix.go, src/pkg/os/error_windows.goといった各プラットフォーム固有のファイルには、それぞれのOSでエラーを判定するための具体的なロジックが実装されています。
    • 変更前は、これらのファイルにもIsExist, IsNotExist, IsPermissionという公開関数が定義され、それぞれにドキュメントが付与されていました。
    • 変更後、これらのファイル内の関数は、isExist, isNotExist, isPermissionというように、最初の文字を小文字に変更することで「非公開 (unexported)」関数となりました。これにより、これらの関数はosパッケージの内部からのみ呼び出すことができ、外部からは直接アクセスできなくなります。
    • 非公開化された関数からは、重複していたドキュメントコメントが削除されました。
  3. 共通関数からの呼び出し:

    • src/pkg/os/error.goに定義された公開関数IsExist, IsNotExist, IsPermissionは、それぞれ対応する非公開関数isExist, isNotExist, isPermissionを呼び出すように変更されました。
    • Goのビルドシステムは、ターゲットOSに応じて適切なプラットフォーム固有のファイル(例: Linuxビルド時にはerror_posix.go)をコンパイルに含めます。これにより、error.goの公開関数が呼び出す非公開関数は、常に現在のOSに合った実装がリンクされることになります。

この変更により、外部から見えるAPIのインターフェースとドキュメントはerror.goに一元化され、内部的なプラットフォーム依存の実装は非公開関数として隠蔽されました。これは、APIの安定性を保ちつつ、内部実装の柔軟性を高めるというGoの設計思想に合致しています。

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

src/pkg/os/error.go の変更

--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -42,3 +42,21 @@ func NewSyscallError(syscall string, err error) error {
 	}
 	return &SyscallError{syscall, err}
 }
+
+// IsExist returns whether the error is known to report that a file already exists.
+// It is satisfied by ErrExist as well as some syscall errors.
+func IsExist(err error) bool {
+	return isExist(err)
+}
+
+// IsNotExist returns whether the error is known to report that a file does not exist.
+// It is satisfied by ErrNotExist as well as some syscall errors.
+func IsNotExist(err error) bool {
+	return isNotExist(err)
+}
+
+// IsPermission returns whether the error is known to report that permission is denied.
+// It is satisfied by ErrPermission as well as some syscall errors.
+func IsPermission(err error) bool {
+	return isPermission(err)
+}

この変更では、IsExist, IsNotExist, IsPermissionという公開関数が追加され、それぞれの関数に詳細なドキュメントコメントが付与されています。これらの関数は、対応する非公開関数(isExist, isNotExist, isPermission)を呼び出すだけです。

src/pkg/os/error_posix.go の変更例

--- a/src/pkg/os/error_posix.go
+++ b/src/pkg/os/error_posix.go
@@ -8,27 +8,21 @@ package os
 
 import "syscall"
 
-// IsExist returns whether the error is known to report that a file already exists.
-// It is satisfied by ErrExist as well as some syscall errors.
-func IsExist(err error) bool {
+func isExist(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}
 	return err == syscall.EEXIST || err == ErrExist
 }
 
-// IsNotExist returns whether the error is known to report that a file does not exist.
-// It is satisfied by ErrNotExist as well as some syscall errors.
-func IsNotExist(err error) bool {
+func isNotExist(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}
 	return err == syscall.ENOENT || err == ErrNotExist
 }
 
-// IsPermission returns whether the error is known to report that permission is denied.
-// It is satisfied by ErrPermission as well as some syscall errors.
-func IsPermission(err error) bool {
+func isPermission(err error) bool {
 	if pe, ok := err.(*PathError); ok {
 		err = pe.Err
 	}

この変更は、error_posix.go(およびerror_plan9.go, error_windows.goも同様)において、公開関数であったIsExist, IsNotExist, IsPermissionの名前をisExist, isNotExist, isPermissionという非公開名に変更し、それに伴い重複していたドキュメントコメントを削除しています。

コアとなるコードの解説

このコミットの核心は、Go言語のパッケージ設計における「公開APIと内部実装の分離」を徹底した点にあります。

変更前は、IsExistなどのエラー述語関数は、src/pkg/os/error.go(共通部分)と、各プラットフォーム固有のファイル(error_posix.goなど)の両方で公開関数として定義されていました。これは、各ファイルがそれぞれ独立してコンパイルされることを前提とした設計の名残である可能性があります。しかし、これにより、同じ公開APIに対するドキュメントが複数箇所に存在し、保守上の問題を引き起こしていました。

このコミットでは、以下の戦略が取られました。

  1. 公開APIの単一化: src/pkg/os/error.goに、IsExist, IsNotExist, IsPermissionという公開関数を定義し、これらの関数にすべてのユーザーが参照すべきドキュメントを記述しました。これにより、これらの関数の振る舞いに関する「真実の情報源」が一つに集約されました。

  2. プラットフォーム固有実装の隠蔽: 各プラットフォーム固有のファイル(error_posix.goなど)に存在していた同名の関数は、isExistのように小文字で始まる非公開関数にリネームされました。これにより、これらの関数はosパッケージの内部でのみ利用可能となり、外部からは直接呼び出せなくなりました。また、これらの非公開関数からは、公開APIのドキュメントと重複するコメントが削除されました。

  3. 共通APIから内部実装への委譲: src/pkg/os/error.goの公開関数は、単にそれぞれの非公開関数を呼び出すだけの薄いラッパーとなりました。Goのビルドシステムは、ターゲットOSに応じて適切なプラットフォーム固有のファイル(例えば、Linux向けビルドではerror_posix.go)をコンパイルに含めるため、IsExistが呼び出すisExistは、常にそのOSに特化した正しい実装に解決されます。

このアプローチにより、osパッケージの外部から見たAPIはシンプルかつ一貫性を保ちつつ、内部的にはOSごとの差異を吸収する柔軟な構造が実現されました。ドキュメントの重複が解消されたことで、将来的なメンテナンスコストが削減され、ドキュメントの正確性が向上しました。

関連リンク

参考にした情報源リンク

  • Go言語の公式ドキュメント(osパッケージ、エラーハンドリング、パッケージの公開/非公開ルールに関する一般的な情報)
  • Go言語のソースコード(src/pkg/os/ディレクトリ内の関連ファイル)
  • Gitのコミットログと差分情報