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

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

このコミットは、Go言語の標準ライブラリであるosパッケージにおけるエラーハンドリング、特にPathError型に関するドキュメントを改善することを目的としています。ファイルシステム操作に関連する関数がエラーを返す際に、そのエラーが*PathError型であることを明示するコメントが追加されています。これにより、開発者がosパッケージの関数から返されるエラーの型をより正確に理解し、適切なエラーハンドリングを実装できるようになります。

コミット

  • コミットハッシュ: be0f6febad2111ba1b2e95c61b21389d9ba1d400
  • Author: Rob Pike r@golang.org
  • Date: Thu Feb 9 16:55:36 2012 +1100

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

https://github.com/golang/go/commit/be0f6febad2111ba1b2e95c61b21389d9ba1d400

元コミット内容

os: talk about errors and PathError in the package documentation

Fixes #2383.

R=golang-dev, bradfitz, adg, rsc
CC=golang-dev
https://golang.org/cl/5641061

変更の背景

このコミットの背景には、Go言語のosパッケージが提供するファイルシステム操作関数が返すエラーの型に関する明確性の欠如がありました。特に、ファイルパスに関連する操作(ファイルのオープン、ディレクトリの作成、ファイル情報の取得など)でエラーが発生した場合、Goの慣習としてerrorインターフェース型の値が返されます。しかし、その具体的な基底型が何であるか、そしてその型からどのような追加情報(例えば、エラーが発生した操作の種類やファイルパス)が取得できるのかが、ドキュメント上で十分に説明されていませんでした。

GoのIssue 2383(code.google.com/p/go/issues/detail?id=2383)は、ファイルが存在するかどうかを直接確認する関数がないこと、そしてファイルが存在しない場合にos.ENOENTのようなエラーを処理する必要があることについて議論していました。この議論は、Goのエラーハンドリングがエラーコードではなくerrorインターフェースを返すという設計思想に基づいています。このコミットは、この設計思想を補完し、特にファイルパスに関連するエラーの場合には、より詳細な情報を提供する*PathError型が返されることを明示することで、開発者がより堅牢なエラーハンドリングコードを書けるようにすることを目的としています。

前提知識の解説

Go言語のエラーハンドリング

Go言語では、エラーは組み込みのerrorインターフェースによって表現されます。このインターフェースは、Error() stringという単一のメソッドを持ち、エラーメッセージを文字列として返します。関数がエラーを返す場合、通常は戻り値の最後の要素としてerror型を返します。エラーが発生しなかった場合はnilを返します。

func someFunction() (resultType, error) {
    // ... 処理 ...
    if someErrorCondition {
        return zeroValue, errors.New("something went wrong")
    }
    return actualResult, nil
}

PathError

PathErrorは、Goのosパッケージ(およびio/fsパッケージ)で定義されている特定のエラー型です。これは、ファイルシステム操作中に発生したエラーに関する追加のコンテキストを提供するために使用されます。PathError構造体は通常、以下のフィールドを持ちます。

  • Op (string): 失敗した操作の種類(例: "open", "read", "write", "mkdir"など)。
  • Path (string): エラーが発生したファイルまたはディレクトリのパス。
  • Err (error): 根本的なエラー(例: "no such file or directory", "permission denied"など、syscall.Errnoなどのシステムコールエラー)。

PathErrorerrorインターフェースを実装しているため、通常のerrorとして扱うことができます。しかし、型アサーション(if pathErr, ok := err.(*os.PathError); ok { ... })やGo 1.13以降で導入されたerrors.As()関数を使用することで、PathErrorの具体的なフィールドにアクセスし、より詳細なエラーハンドリングを行うことが可能です。

例えば、ファイルが存在しないためにos.Openが失敗した場合、返されるエラーは*os.PathError型であり、そのPathフィールドには開こうとしたファイル名が、Errフィールドにはsyscall.ENOENT(No such file or directory)のようなエラーが格納されます。

技術的詳細

このコミットの主要な変更は、osパッケージ内の様々なファイルシステム操作関数のドキュメントコメントに、エラーが発生した場合に返されるエラーの型が*PathErrorであることを明示する記述を追加したことです。これは、Goのドキュメンテーション慣習に従い、関数の振る舞いをより明確にするためのものです。

具体的には、以下の種類の関数に対してドキュメントの修正が行われました。

  1. プロセス関連関数: os.StartProcessos.Execなど、新しいプロセスを開始したり、現在のプロセスを置き換えたりする関数。これらの関数がパスに関連するエラーを返す場合に*PathError型となることが明記されました。
  2. ファイル操作関数: os.Mkdir, os.Chdir, os.Open, os.Create, os.OpenFile, os.Truncate, os.Remove, os.Rename, os.Chmod, os.Chown, os.Lchown, os.Chtimesなど、ファイルやディレクトリの作成、変更、削除、属性変更などを行う関数。これらの関数がパスに関連するエラーを返す場合に*PathError型となることが明記されました。
  3. ファイル情報取得関数: os.Stat, os.Lstat, os.Readlinkなど、ファイルやシンボリックリンクの情報を取得する関数。これらの関数がパスに関連するエラーを返す場合に*PathError型となることが明記されました。

これらの変更は、src/pkg/os/ディレクトリ内の複数のファイル(exec_plan9.go, exec_posix.go, file.go, file_plan9.go, file_posix.go, file_unix.go, file_windows.go, stat_plan9.go, stat_windows.go)にわたって行われています。これは、osパッケージが様々なオペレーティングシステム(Plan 9, POSIX準拠システム、Unix系システム、Windows)に対応するために、プラットフォーム固有の実装ファイルを持っているためです。各プラットフォーム固有のファイルにおいても、共通のインターフェースを持つ関数に対して同様のドキュメント修正が適用されています。

この変更は、コードの振る舞いを変更するものではなく、あくまでドキュメントの正確性と明確性を向上させるものです。これにより、osパッケージを利用する開発者は、エラーハンドリングの際に*PathErrorの存在を認識し、必要に応じてその詳細情報を利用できるようになります。

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

変更は主に、osパッケージ内の関数のドキュメントコメントに、以下の形式の行を追加する形で行われています。

// If there is an error, it will be of type *PathError.

以下に、いくつかの代表的な変更箇所を抜粋します。

src/pkg/os/file.go のパッケージドキュメントの変更:

--- a/src/pkg/os/file.go
+++ b/src/pkg/os/file.go
@@ -3,7 +3,13 @@
 // license that can be found in the LICENSE file.
 
 // Package os provides a platform-independent interface to operating system
-// functionality.  The design is Unix-like.\n+// functionality. The design is Unix-like, although the error handling is
+// Go-like; failing calls return values of type error rather than error numbers.
+// Often, more information is available within the error. For example,
+// if a call that takes a file name fails, such as Open or Stat, the error
+// will include failing file name when printed and will be of type *PathError,
+// which may be unpacked for more information.
+//
 // The os interface is intended to be uniform across all operating systems.
 // Features not generally available appear in the system-specific package syscall.
 package os

src/pkg/os/exec_plan9.goStartProcess 関数の変更:

--- a/src/pkg/os/exec_plan9.go
+++ b/src/pkg/os/exec_plan9.go
@@ -12,6 +12,7 @@ import (
 
 // StartProcess starts a new process with the program, arguments and attributes
 // specified by name, argv and attr.
+// If there is an error, it will be of type *PathError.
 func StartProcess(name string, argv []string, attr *ProcAttr) (p *Process, err error) {
 	sysattr := &syscall.ProcAttr{
 		Dir: attr.Dir,

src/pkg/os/file.goMkdir 関数の変更:

--- a/src/pkg/os/file.go
+++ b/src/pkg/os/file.go
@@ -157,7 +163,7 @@ func (f *File) WriteString(s string) (ret int, err error) {
 }
 
 // Mkdir creates a new directory with the specified name and permission bits.
-// It returns an error, if any.\n+// If there is an error, it will be of type *PathError.
 func Mkdir(name string, perm FileMode) error {
 	e := syscall.Mkdir(name, syscallMode(perm))
 	if e != nil {

src/pkg/os/file_unix.goStat 関数の変更:

--- a/src/pkg/os/file_unix.go
+++ b/src/pkg/os/file_unix.go
@@ -113,11 +113,12 @@ func (f *File) Stat() (fi FileInfo, err error) {
 	return fileInfoFromStat(&stat, f.name), nil
 }
 
-// Stat returns a FileInfo describing the named file and an error, if any.\n+// Stat returns a FileInfo describing the named file.
 // If name names a valid symbolic link, the returned FileInfo describes
 // the file pointed at by the link and has fi.FollowedSymlink set to true.
 // If name names an invalid symbolic link, the returned FileInfo describes
 // the link itself and has fi.FollowedSymlink set to false.
+// If there is an error, it will be of type *PathError.
 func Stat(name string) (fi FileInfo, err error) {
 	var stat syscall.Stat_t
 	err = syscall.Stat(name, &stat)

コアとなるコードの解説

これらの変更は、Goのosパッケージのドキュメントの品質とユーザビリティを大幅に向上させます。

  1. パッケージレベルのドキュメントの改善: src/pkg/os/file.goのパッケージコメントに、Goのエラーハンドリングの哲学(エラー番号ではなくerror型を返す)と、特にファイル名に関連するエラーが*PathError型として返され、そこから詳細な情報が取得できることが明記されました。これは、パッケージ全体の振る舞いを理解するための重要なガイダンスとなります。
  2. 関数レベルのドキュメントの明確化: StartProcess, Mkdir, Open, Statなど、多くのファイルシステム操作関数に対して「If there is an error, it will be of type *PathError.」というコメントが追加されました。これにより、各関数がどのような種類のエラーを返す可能性があるのかが明確になり、開発者は以下のようなメリットを享受できます。
    • 予測可能性の向上: 開発者は、特定のos関数が*PathErrorを返すことを事前に知ることができ、エラーハンドリングの設計をより正確に行えます。
    • 堅牢なエラーハンドリング: *PathErrorの存在を知ることで、開発者は型アサーションやerrors.As()を使用して、エラーの原因(操作、パス、根本的なエラー)を詳細に分析し、それに応じたリカバリロジックやエラーメッセージの生成を行うことができます。例えば、ファイルが存在しないエラーとパーミッションエラーを区別して処理することが容易になります。
    • デバッグの容易化: エラーメッセージに加えて、PathErrorが提供するOpPathの情報は、問題の特定とデバッグを大いに助けます。

これらのドキュメントの追加は、Go言語の「エラーは値である」という哲学を補完し、エラーからより多くの情報を引き出すための道筋を示しています。

関連リンク

参考にした情報源リンク