[インデックス 12632] ファイルの概要
このコミットは、Go言語の os
パッケージにおけるエラーハンドリング、特にファイルやディレクトリの存在確認に関する改善を目的としています。Windows環境において、os.IsNotExist()
関数が ERROR_PATH_NOT_FOUND
エラーも適切に処理するように修正され、また os.IsExist()
および os.IsNotExist()
のドキュメントが、ファイルだけでなくディレクトリにも適用されることを明確にするように更新されています。さらに、関連するエラー判定ロジックが整理されています。
コミット
commit 24ed667b33aa634081a4f562f1503c758a208ddc
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Mar 14 23:54:40 2012 +0800
os: IsNotExist() should also consider ERROR_PATH_NOT_FOUND on Windows
Also update documentation about IsExist() and IsNotExist(), they are not
about files only.
R=rsc
CC=golang-dev
https://golang.org/cl/5794073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/24ed667b33aa634081a4f562f1503c758a208ddc
元コミット内容
os: IsNotExist()
はWindows上で ERROR_PATH_NOT_FOUND
も考慮すべきである。
また、IsExist()
と IsNotExist()
のドキュメントを更新し、これらがファイルだけでなくディレクトリにも関連することを明確にする。
変更の背景
Go言語の os
パッケージには、ファイルやディレクトリの存在確認を行うためのヘルパー関数 os.IsExist()
と os.IsNotExist()
が提供されています。これらの関数は、基盤となるシステムコールが返す様々なエラーコードを抽象化し、より汎用的な「存在するか」「存在しないか」という概念にマッピングします。
しかし、Windows環境において、特定のシナリオ(例えば、存在しないディレクトリへの Chdir
試行など)では、ファイルが存在しないことを示す ERROR_FILE_NOT_FOUND
ではなく、パスが存在しないことを示す ERROR_PATH_NOT_FOUND
というエラーが返されることがありました。従来の os.IsNotExist()
はこの ERROR_PATH_NOT_FOUND
を適切に処理していなかったため、Windows上でパスが存在しない場合に os.IsNotExist()
が true
を返さないという不整合が生じていました。
このコミットは、この不整合を解消し、Windows上での os.IsNotExist()
の挙動をより堅牢で期待通りにするために行われました。また、これらの関数がファイルだけでなくディレクトリの存在確認にも利用できることを明確にするため、ドキュメントの更新も同時に行われています。
前提知識の解説
- Go言語の
os
パッケージ: Goの標準ライブラリの一部で、オペレーティングシステムとのインタラクション(ファイル操作、プロセス管理、環境変数など)を提供します。 os.IsExist(err error) bool
: 引数として渡されたエラーが、ファイルやディレクトリが「既に存在する」ことを示すエラーである場合にtrue
を返します。os.IsNotExist(err error) bool
: 引数として渡されたエラーが、ファイルやディレクトリが「存在しない」ことを示すエラーである場合にtrue
を返します。os.IsPermission(err error) bool
: 引数として渡されたエラーが、アクセス権限の問題を示すエラーである場合にtrue
を返します。syscall
パッケージ: Goの標準ライブラリの一部で、低レベルのシステムコールへのアクセスを提供します。オペレーティングシステム固有のエラーコード(例:syscall.ENOENT
,syscall.EEXIST
など)が定義されています。- Windowsシステムエラーコード: Windows APIが返すエラーコードで、特定の状況を示します。
ERROR_FILE_NOT_FOUND
(0x2): 指定されたファイルが見つかりません。ERROR_PATH_NOT_FOUND
(0x3): 指定されたパスが見つかりません。これは、ファイル自体ではなく、パスの途中のディレクトリが存在しない場合に発生します。ERROR_ALREADY_EXISTS
(0xB7): 指定されたファイルは既に存在します。ERROR_ACCESS_DENIED
(0x5): アクセスが拒否されました。
- POSIX (Portable Operating System Interface): Unix系OSの標準インターフェース。POSIX準拠のシステムでは、ファイルやディレクトリが存在しない場合、通常
ENOENT
(Error NO ENTry) が返されます。ファイルが既に存在する場合はEEXIST
(Error EXIST) が返されます。 os.PathError
:os
パッケージで定義されているエラー型の一つで、パスに関連する操作で発生したエラーをラップします。Op
(操作名)、Path
(パス)、Err
(元のエラー) のフィールドを持ちます。
技術的詳細
このコミットの主要な変更点は、Windows固有のエラーハンドリングロジックが記述されている src/pkg/os/error_windows.go
ファイルにあります。
-
isNotExist
関数の修正:- 変更前:
return err == syscall.ENOENT || err == ErrNotExist
- 変更後:
return err == syscall.ERROR_FILE_NOT_FOUND || err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
この変更により、Windows上でos.IsNotExist()
が、ファイルが見つからないことを示すERROR_FILE_NOT_FOUND
に加えて、パスが見つからないことを示すERROR_PATH_NOT_FOUND
も「存在しない」エラーとして認識するようになりました。これにより、例えば存在しないディレクトリへのos.Chdir()
呼び出しがERROR_PATH_NOT_FOUND
を返した場合でも、os.IsNotExist()
が正しくtrue
を返すようになります。
- 変更前:
-
isExist
関数の修正:- 変更前:
return err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist
- 変更後:
return err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist
syscall.EEXIST
はPOSIXシステムで使われるエラーコードであり、Windows固有のerror_windows.go
からは削除されました。これは、Windowsのエラーマッピングをより正確にするための整理と考えられます。WindowsではERROR_ALREADY_EXISTS
やERROR_FILE_EXISTS
が同等の意味を持ちます。
- 変更前:
-
isPermission
関数の修正:- 変更前:
return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
- 変更後:
return err == ErrPermission
同様に、syscall.EACCES
(アクセス拒否) とsyscall.EPERM
(操作が許可されていません) もPOSIXシステムで使われるエラーコードであり、Windows固有のファイルから削除されました。WindowsではErrPermission
がより抽象化された権限エラーを示します。
- 変更前:
-
ドキュメントの更新 (
src/pkg/os/error.go
):IsExist
とIsNotExist
のコメントが、「ファイル」だけでなく「ファイルまたはディレクトリ」に適用されることを明確にするように変更されました。これにより、これらの関数の適用範囲がより正確に伝わるようになりました。
-
テストケースの追加 (
src/pkg/os/error_test.go
):TestErrIsNotExist
という新しいテスト関数が追加されました。このテストは、一時ディレクトリ内に存在しないファイルやディレクトリパスを作成し、os.Open()
やos.Chdir()
を試行してos.IsNotExist()
が正しく機能するかを検証します。特に、ネストされた存在しないパス(例:/tmp/nonexistent_dir/nonexistent_file
)に対してもテストが行われ、ERROR_PATH_NOT_FOUND
のシナリオがカバーされていることを確認しています。
これらの変更により、Goの os
パッケージは、異なるオペレーティングシステム間でのエラーハンドリングの一貫性を高め、特にWindows環境での堅牢性を向上させています。
コアとなるコードの変更箇所
src/pkg/os/error.go
--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -43,14 +43,14 @@ 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.
+// IsExist returns whether the error is known to report that a file or directory
+// 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.
+// IsNotExist returns whether the error is known to report that a file or directory
+// does not exist. It is satisfied by ErrNotExist as well as some syscall errors.
func IsNotExist(err error) bool {
return isNotExist(err)
}
src/pkg/os/error_test.go
--- a/src/pkg/os/error_test.go
+++ b/src/pkg/os/error_test.go
@@ -5,8 +5,10 @@
package os_test
import (
+\t"fmt"
"io/ioutil"
"os"
+\t"path/filepath"
"testing"
)
@@ -24,8 +26,56 @@ func TestErrIsExist(t *testing.T) {
t.Fatal("Open should have failed")
return
}
-\tif !os.IsExist(err) {
-\t\tt.Fatalf("os.IsExist does not work as expected for %#v", err)
+\tif s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
+\t\tt.Fatal(s)
return
}
}
+\n+func testErrNotExist(name string) string {
+\tf, err := os.Open(name)
+\tif err == nil {
+\t\tf.Close()
+\t\treturn "Open should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\n+\terr = os.Chdir(name)
+\tif err == nil {
+\t\treturn "Chdir should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\treturn ""
+}\n+\n+func TestErrIsNotExist(t *testing.T) {
+\ttmpDir, err := ioutil.TempDir("", "_Go_ErrIsNotExist")
+\tif err != nil {
+\t\tt.Fatalf("create ErrIsNotExist tempdir: %s", err)
+\t\treturn
+\t}\n+\tdefer os.RemoveAll(tmpDir)
+\n+\tname := filepath.Join(tmpDir, "NotExists")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+\n+\tname = filepath.Join(name, "NotExists2")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+}\n+\n+func checkErrorPredicate(predName string, pred func(error) bool, err error) string {
+\tif !pred(err) {
+\t\treturn fmt.Sprintf("%s does not work as expected for %#v", predName, err)
+\t}\n+\treturn ""
+}\n```
### `src/pkg/os/error_windows.go`
```diff
--- a/src/pkg/os/error_windows.go
+++ b/src/pkg/os/error_windows.go
@@ -10,7 +10,7 @@ func isExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS ||
+\treturn err == syscall.ERROR_ALREADY_EXISTS ||
\t\terr == syscall.ERROR_FILE_EXISTS || err == ErrExist
}
@@ -18,12 +18,13 @@ func isNotExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.ENOENT || err == ErrNotExist
+\treturn err == syscall.ERROR_FILE_NOT_FOUND ||
+\t\terr == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
}
func isPermission(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
+\treturn err == ErrPermission
}
コアとなるコードの解説
-
src/pkg/os/error.go
:IsExist
とIsNotExist
関数のコメントが修正され、これらの関数がファイルだけでなくディレクトリの存在確認にも使用できることが明示されました。これは、関数の意図と適用範囲をより正確に伝えるための重要なドキュメント改善です。
-
src/pkg/os/error_test.go
:testErrNotExist
ヘルパー関数が追加され、os.Open
とos.Chdir
の両方でos.IsNotExist
が正しく動作するかを検証します。特にos.Chdir
はディレクトリ操作であり、ERROR_PATH_NOT_FOUND
が発生しやすいシナリオをカバーします。TestErrIsNotExist
関数は、一時ディレクトリ内に存在しないファイルパスと、ネストされた存在しないディレクトリパスの両方に対してtestErrNotExist
を呼び出し、os.IsNotExist
の堅牢性をテストしています。checkErrorPredicate
ヘルパー関数は、エラー述語(os.IsExist
やos.IsNotExist
など)が期待通りにtrue
を返すかを汎用的にチェックするために導入されました。
-
src/pkg/os/error_windows.go
:isNotExist
関数内で、Windows固有のエラーコードsyscall.ERROR_PATH_NOT_FOUND
がos.IsNotExist
の判定基準に追加されました。これにより、Windows環境でパスが存在しない場合にos.IsNotExist
が正しくtrue
を返すようになります。isExist
関数からsyscall.EEXIST
が削除されました。これはPOSIXシステムのエラーコードであり、Windows固有のロジックからは不要と判断されたためです。isPermission
関数からsyscall.EACCES
とsyscall.EPERM
が削除されました。これらもPOSIXシステムのエラーコードであり、WindowsではErrPermission
がより抽象的な権限エラーを表すため、整理されました。
これらの変更は、Goのクロスプラットフォームなエラーハンドリングをより正確かつ堅牢にするための重要なステップです。特にWindows環境でのファイル・ディレクトリ操作におけるエラーの解釈が改善され、開発者がより信頼性の高いコードを書けるようになりました。
関連リンク
- Go言語の
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語の
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - Windows System Error Codes: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes
参考にした情報源リンク
- https://golang.org/cl/5794073 (元のGo Gerritの変更リスト)
- https://pkg.go.dev/os
- https://pkg.go.dev/syscall
- https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes
- https://en.wikipedia.org/wiki/POSIX
- https://en.wikipedia.org/wiki/Errno
- https://go.dev/doc/effective_go#errors# [インデックス 12632] ファイルの概要
このコミットは、Go言語の os
パッケージにおけるエラーハンドリング、特にファイルやディレクトリの存在確認に関する改善を目的としています。Windows環境において、os.IsNotExist()
関数が ERROR_PATH_NOT_FOUND
エラーも適切に処理するように修正され、また os.IsExist()
および os.IsNotExist()
のドキュメントが、ファイルだけでなくディレクトリにも適用されることを明確にするように更新されています。さらに、関連するエラー判定ロジックが整理されています。
コミット
commit 24ed667b33aa634081a4f562f1503c758a208ddc
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Wed Mar 14 23:54:40 2012 +0800
os: IsNotExist() should also consider ERROR_PATH_NOT_FOUND on Windows
Also update documentation about IsExist() and IsNotExist(), they are not
about files only.
R=rsc
CC=golang-dev
https://golang.org/cl/5794073
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/24ed667b33aa634081a4f562f1503c758a208ddc
元コミット内容
os: IsNotExist()
はWindows上で ERROR_PATH_NOT_FOUND
も考慮すべきである。
また、IsExist()
と IsNotExist()
のドキュメントを更新し、これらがファイルだけでなくディレクトリにも関連することを明確にする。
変更の背景
Go言語の os
パッケージには、ファイルやディレクトリの存在確認を行うためのヘルパー関数 os.IsExist()
と os.IsNotExist()
が提供されています。これらの関数は、基盤となるシステムコールが返す様々なエラーコードを抽象化し、より汎用的な「存在するか」「存在しないか」という概念にマッピングします。
しかし、Windows環境において、特定のシナリオ(例えば、存在しないディレクトリへの Chdir
試行など)では、ファイルが存在しないことを示す ERROR_FILE_NOT_FOUND
ではなく、パスが存在しないことを示す ERROR_PATH_NOT_FOUND
というエラーが返されることがありました。従来の os.IsNotExist()
はこの ERROR_PATH_NOT_FOUND
を適切に処理していなかったため、Windows上でパスが存在しない場合に os.IsNotExist()
が true
を返さないという不整合が生じていました。
このコミットは、この不整合を解消し、Windows上での os.IsNotExist()
の挙動をより堅牢で期待通りにするために行われました。また、これらの関数がファイルだけでなくディレクトリの存在確認にも利用できることを明確にするため、ドキュメントの更新も同時に行われています。
前提知識の解説
- Go言語の
os
パッケージ: Goの標準ライブラリの一部で、オペレーティングシステムとのインタラクション(ファイル操作、プロセス管理、環境変数など)を提供します。 os.IsExist(err error) bool
: 引数として渡されたエラーが、ファイルやディレクトリが「既に存在する」ことを示すエラーである場合にtrue
を返します。os.IsNotExist(err error) bool
: 引数として渡されたエラーが、ファイルやディレクトリが「存在しない」ことを示すエラーである場合にtrue
を返します。os.IsPermission(err error) bool
: 引数として渡されたエラーが、アクセス権限の問題を示すエラーである場合にtrue
を返します。syscall
パッケージ: Goの標準ライブラリの一部で、低レベルのシステムコールへのアクセスを提供します。オペレーティングシステム固有のエラーコード(例:syscall.ENOENT
,syscall.EEXIST
など)が定義されています。- Windowsシステムエラーコード: Windows APIが返すエラーコードで、特定の状況を示します。
ERROR_FILE_NOT_FOUND
(0x2): 指定されたファイルが見つかりません。ERROR_PATH_NOT_FOUND
(0x3): 指定されたパスが見つかりません。これは、ファイル自体ではなく、パスの途中のディレクトリが存在しない場合に発生します。ERROR_ALREADY_EXISTS
(0xB7): 指定されたファイルは既に存在します。ERROR_ACCESS_DENIED
(0x5): アクセスが拒否されました。
- POSIX (Portable Operating System Interface): Unix系OSの標準インターフェース。POSIX準拠のシステムでは、ファイルやディレクトリが存在しない場合、通常
ENOENT
(Error NO ENTry) が返されます。ファイルが既に存在する場合はEEXIST
(Error EXIST) が返されます。 os.PathError
:os
パッケージで定義されているエラー型の一つで、パスに関連する操作で発生したエラーをラップします。Op
(操作名)、Path
(パス)、Err
(元のエラー) のフィールドを持ちます。
技術的詳細
このコミットの主要な変更点は、Windows固有のエラーハンドリングロジックが記述されている src/pkg/os/error_windows.go
ファイルにあります。
-
isNotExist
関数の修正:- 変更前:
return err == syscall.ENOENT || err == ErrNotExist
- 変更後:
return err == syscall.ERROR_FILE_NOT_FOUND || err == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
この変更により、Windows上でos.IsNotExist()
が、ファイルが見つからないことを示すERROR_FILE_NOT_FOUND
に加えて、パスが見つからないことを示すERROR_PATH_NOT_FOUND
も「存在しない」エラーとして認識するようになりました。これにより、例えば存在しないディレクトリへのos.Chdir()
呼び出しがERROR_PATH_NOT_FOUND
を返した場合でも、os.IsNotExist()
が正しくtrue
を返すようになります。
- 変更前:
-
isExist
関数の修正:- 変更前:
return err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist
- 変更後:
return err == syscall.ERROR_ALREADY_EXISTS || err == syscall.ERROR_FILE_EXISTS || err == ErrExist
syscall.EEXIST
はPOSIXシステムで使われるエラーコードであり、Windows固有のerror_windows.go
からは削除されました。これは、Windowsのエラーマッピングをより正確にするための整理と考えられます。WindowsではERROR_ALREADY_EXISTS
やERROR_FILE_EXISTS
が同等の意味を持ちます。
- 変更前:
-
isPermission
関数の修正:- 変更前:
return err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
- 変更後:
return err == ErrPermission
同様に、syscall.EACCES
(アクセス拒否) とsyscall.EPERM
(操作が許可されていません) もPOSIXシステムで使われるエラーコードであり、Windows固有のファイルから削除されました。WindowsではErrPermission
がより抽象化された権限エラーを示します。
- 変更前:
-
ドキュメントの更新 (
src/pkg/os/error.go
):IsExist
とIsNotExist
のコメントが、「ファイル」だけでなく「ファイルまたはディレクトリ」に適用されることを明確にするように変更されました。これにより、これらの関数の適用範囲がより正確に伝わるようになりました。
-
テストケースの追加 (
src/pkg/os/error_test.go
):TestErrIsNotExist
という新しいテスト関数が追加されました。このテストは、一時ディレクトリ内に存在しないファイルやディレクトリパスを作成し、os.Open()
やos.Chdir()
を試行してos.IsNotExist()
が正しく機能するかを検証します。特に、ネストされた存在しないパス(例:/tmp/nonexistent_dir/nonexistent_file
)に対してもテストが行われ、ERROR_PATH_NOT_FOUND
のシナリオがカバーされていることを確認しています。
これらの変更により、Goの os
パッケージは、異なるオペレーティングシステム間でのエラーハンドリングの一貫性を高め、特にWindows環境での堅牢性を向上させています。
コアとなるコードの変更箇所
src/pkg/os/error.go
--- a/src/pkg/os/error.go
+++ b/src/pkg/os/error.go
@@ -43,14 +43,14 @@ 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.
+// IsExist returns whether the error is known to report that a file or directory
+// 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.
+// IsNotExist returns whether the error is known to report that a file or directory
+// does not exist. It is satisfied by ErrNotExist as well as some syscall errors.
func IsNotExist(err error) bool {
return isNotExist(err)
}
src/pkg/os/error_test.go
--- a/src/pkg/os/error_test.go
+++ b/src/pkg/os/error_test.go
@@ -5,8 +5,10 @@
package os_test
import (
+\t"fmt"
"io/ioutil"
"os"
+\t"path/filepath"
"testing"
)
@@ -24,8 +26,56 @@ func TestErrIsExist(t *testing.T) {
t.Fatal("Open should have failed")
return
}
-\tif !os.IsExist(err) {
-\t\tt.Fatalf("os.IsExist does not work as expected for %#v", err)
+\tif s := checkErrorPredicate("os.IsExist", os.IsExist, err); s != "" {
+\t\tt.Fatal(s)
return
}
}
+\n+func testErrNotExist(name string) string {
+\tf, err := os.Open(name)
+\tif err == nil {
+\t\tf.Close()
+\t\treturn "Open should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\n+\terr = os.Chdir(name)
+\tif err == nil {
+\t\treturn "Chdir should have failed"
+\t}
+\tif s := checkErrorPredicate("os.IsNotExist", os.IsNotExist, err); s != "" {
+\t\treturn s
+\t}
+\treturn ""
+}\n+\n+func TestErrIsNotExist(t *testing.T) {
+\ttmpDir, err := ioutil.TempDir("", "_Go_ErrIsNotExist")
+\tif err != nil {
+\t\tt.Fatalf("create ErrIsNotExist tempdir: %s", err)
+\t\treturn
+\t}\n+\tdefer os.RemoveAll(tmpDir)
+\n+\tname := filepath.Join(tmpDir, "NotExists")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+\n+\tname = filepath.Join(name, "NotExists2")
+\tif s := testErrNotExist(name); s != "" {
+\t\tt.Fatal(s)
+\t\treturn
+\t}\n+}\n+\n+func checkErrorPredicate(predName string, pred func(error) bool, err error) string {
+\tif !pred(err) {
+\t\treturn fmt.Sprintf("%s does not work as expected for %#v", predName, err)
+\t}\n+\treturn ""
+}\n```
### `src/pkg/os/error_windows.go`
```diff
--- a/src/pkg/os/error_windows.go
+++ b/src/pkg/os/error_windows.go
@@ -10,7 +10,7 @@ func isExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.EEXIST || err == syscall.ERROR_ALREADY_EXISTS ||
+\treturn err == syscall.ERROR_ALREADY_EXISTS ||
\t\terr == syscall.ERROR_FILE_EXISTS || err == ErrExist
}
@@ -18,12 +18,13 @@ func isNotExist(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.ENOENT || err == ErrNotExist
+\treturn err == syscall.ERROR_FILE_NOT_FOUND ||
+\t\terr == syscall.ERROR_PATH_NOT_FOUND || err == ErrNotExist
}
func isPermission(err error) bool {
if pe, ok := err.(*PathError); ok {
err = pe.Err
}
-\treturn err == syscall.EACCES || err == syscall.EPERM || err == ErrPermission
+\treturn err == ErrPermission
}
コアとなるコードの解説
-
src/pkg/os/error.go
:IsExist
とIsNotExist
関数のコメントが修正され、これらの関数がファイルだけでなくディレクトリの存在確認にも使用できることが明示されました。これは、関数の意図と適用範囲をより正確に伝えるための重要なドキュメント改善です。
-
src/pkg/os/error_test.go
:testErrNotExist
ヘルパー関数が追加され、os.Open
とos.Chdir
の両方でos.IsNotExist
が正しく動作するかを検証します。特にos.Chdir
はディレクトリ操作であり、ERROR_PATH_NOT_FOUND
が発生しやすいシナリオをカバーします。TestErrIsNotExist
関数は、一時ディレクトリ内に存在しないファイルパスと、ネストされた存在しないディレクトリパスの両方に対してtestErrNotExist
を呼び出し、os.IsNotExist
の堅牢性をテストしています。checkErrorPredicate
ヘルパー関数は、エラー述語(os.IsExist
やos.IsNotExist
など)が期待通りにtrue
を返すかを汎用的にチェックするために導入されました。
-
src/pkg/os/error_windows.go
:isNotExist
関数内で、Windows固有のエラーコードsyscall.ERROR_PATH_NOT_FOUND
がos.IsNotExist
の判定基準に追加されました。これにより、Windows環境でパスが存在しない場合にos.IsNotExist
が正しくtrue
を返すようになります。isExist
関数からsyscall.EEXIST
が削除されました。これはPOSIXシステムのエラーコードであり、Windows固有のロジックからは不要と判断されたためです。isPermission
関数からsyscall.EACCES
とsyscall.EPERM
が削除されました。これらもPOSIXシステムのエラーコードであり、WindowsではErrPermission
がより抽象的な権限エラーを表すため、整理されました。
これらの変更は、Goのクロスプラットフォームなエラーハンドリングをより正確かつ堅牢にするための重要なステップです。特にWindows環境でのファイル・ディレクトリ操作におけるエラーの解釈が改善され、開発者がより信頼性の高いコードを書けるようになりました。
関連リンク
- Go言語の
os
パッケージドキュメント: https://pkg.go.dev/os - Go言語の
syscall
パッケージドキュメント: https://pkg.go.dev/syscall - Windows System Error Codes: https://learn.microsoft.com/en-us/windows/win32/debug/system-error-codes